From 3eacb0c715b09242fa48d6158803d6af09059a02 Mon Sep 17 00:00:00 2001 From: Pierce Date: Fri, 10 Feb 2023 04:52:53 -0800 Subject: [PATCH 1/5] Fixed typo in nfc_magic_scene_wrong_card.c (#2382) --- .../plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c index 69bf9eb50..4b8089693 100644 --- a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c @@ -26,7 +26,7 @@ void nfc_magic_scene_wrong_card_on_enter(void* context) { AlignLeft, AlignTop, FontSecondary, - "Writing is supported\nonly for 4 bytes UID\nMifare CLassic 1k"); + "Writing is supported\nonly for 4 bytes UID\nMifare Classic 1k"); widget_add_button_element( widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wrong_card_widget_callback, nfc_magic); From e7d01998c1ccefb260ddfd1f0e759457ea75006d Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 10 Feb 2023 17:28:26 +0100 Subject: [PATCH 2/5] Update subghz --- applications/main/subghz/SConscript | 32 + applications/main/subghz/application.fam | 2 +- applications/main/subghz/blocks/const.c | 1 + applications/main/subghz/blocks/const.h | 20 + applications/main/subghz/blocks/decoder.c | 27 + applications/main/subghz/blocks/decoder.h | 47 + applications/main/subghz/blocks/encoder.c | 58 + applications/main/subghz/blocks/encoder.h | 67 + applications/main/subghz/blocks/generic.c | 120 ++ applications/main/subghz/blocks/generic.h | 59 + applications/main/subghz/blocks/math.c | 244 ++++ applications/main/subghz/blocks/math.h | 222 ++++ applications/main/subghz/environment.c | 116 ++ applications/main/subghz/environment.h | 115 ++ .../main/subghz/helpers/subghz_custom_event.h | 1 - .../subghz_frequency_analyzer_worker.c | 57 +- .../main/subghz/protocols/alutech_at_4n.c | 455 +++++++ .../main/subghz/protocols/alutech_at_4n.h | 74 ++ applications/main/subghz/protocols/ansonic.c | 346 +++++ applications/main/subghz/protocols/ansonic.h | 107 ++ applications/main/subghz/protocols/base.c | 62 + applications/main/subghz/protocols/base.h | 88 ++ applications/main/subghz/protocols/bett.c | 342 +++++ applications/main/subghz/protocols/bett.h | 107 ++ applications/main/subghz/protocols/bin_raw.c | 1120 +++++++++++++++++ applications/main/subghz/protocols/bin_raw.h | 111 ++ applications/main/subghz/protocols/came.c | 347 +++++ applications/main/subghz/protocols/came.h | 107 ++ .../main/subghz/protocols/came_atomo.c | 598 +++++++++ .../main/subghz/protocols/came_atomo.h | 110 ++ .../main/subghz/protocols/came_twee.c | 468 +++++++ .../main/subghz/protocols/came_twee.h | 107 ++ .../main/subghz/protocols/chamberlain_code.c | 499 ++++++++ .../main/subghz/protocols/chamberlain_code.h | 107 ++ applications/main/subghz/protocols/clemsa.c | 365 ++++++ applications/main/subghz/protocols/clemsa.h | 107 ++ applications/main/subghz/protocols/doitrand.c | 356 ++++++ applications/main/subghz/protocols/doitrand.h | 107 ++ applications/main/subghz/protocols/dooya.c | 447 +++++++ applications/main/subghz/protocols/dooya.h | 107 ++ applications/main/subghz/protocols/faac_slh.c | 512 ++++++++ applications/main/subghz/protocols/faac_slh.h | 129 ++ applications/main/subghz/protocols/gate_tx.c | 334 +++++ applications/main/subghz/protocols/gate_tx.h | 107 ++ applications/main/subghz/protocols/holtek.c | 374 ++++++ applications/main/subghz/protocols/holtek.h | 107 ++ .../main/subghz/protocols/holtek_ht12x.c | 400 ++++++ .../main/subghz/protocols/holtek_ht12x.h | 107 ++ .../main/subghz/protocols/honeywell_wdb.c | 399 ++++++ .../main/subghz/protocols/honeywell_wdb.h | 111 ++ applications/main/subghz/protocols/hormann.c | 341 +++++ applications/main/subghz/protocols/hormann.h | 107 ++ applications/main/subghz/protocols/ido.c | 234 ++++ applications/main/subghz/protocols/ido.h | 73 ++ .../main/subghz/protocols/intertechno_v3.c | 472 +++++++ .../main/subghz/protocols/intertechno_v3.h | 111 ++ applications/main/subghz/protocols/keeloq.c | 1115 ++++++++++++++++ applications/main/subghz/protocols/keeloq.h | 153 +++ .../main/subghz/protocols/keeloq_common.c | 142 +++ .../main/subghz/protocols/keeloq_common.h | 100 ++ applications/main/subghz/protocols/kia.c | 279 ++++ applications/main/subghz/protocols/kia.h | 73 ++ .../subghz/protocols/kinggates_stylo_4k.c | 581 +++++++++ .../subghz/protocols/kinggates_stylo_4k.h | 110 ++ applications/main/subghz/protocols/linear.c | 352 ++++++ applications/main/subghz/protocols/linear.h | 107 ++ .../main/subghz/protocols/linear_delta3.c | 359 ++++++ .../main/subghz/protocols/linear_delta3.h | 111 ++ applications/main/subghz/protocols/magellan.c | 445 +++++++ applications/main/subghz/protocols/magellan.h | 107 ++ applications/main/subghz/protocols/marantec.c | 393 ++++++ applications/main/subghz/protocols/marantec.h | 107 ++ applications/main/subghz/protocols/megacode.c | 429 +++++++ applications/main/subghz/protocols/megacode.h | 107 ++ .../main/subghz/protocols/nero_radio.c | 397 ++++++ .../main/subghz/protocols/nero_radio.h | 107 ++ .../main/subghz/protocols/nero_sketch.c | 382 ++++++ .../main/subghz/protocols/nero_sketch.h | 107 ++ applications/main/subghz/protocols/nice_flo.c | 330 +++++ applications/main/subghz/protocols/nice_flo.h | 107 ++ .../main/subghz/protocols/nice_flor_s.c | 694 ++++++++++ .../main/subghz/protocols/nice_flor_s.h | 127 ++ .../main/subghz/protocols/phoenix_v2.c | 339 +++++ .../main/subghz/protocols/phoenix_v2.h | 107 ++ .../main/subghz/protocols/power_smart.c | 394 ++++++ .../main/subghz/protocols/power_smart.h | 107 ++ .../main/subghz/protocols/princeton.c | 374 ++++++ .../main/subghz/protocols/princeton.h | 115 ++ .../subghz/protocols/princeton_for_testing.c | 288 +++++ .../subghz/protocols/princeton_for_testing.h | 97 ++ .../main/subghz/protocols/protocol_items.c | 50 + .../main/subghz/protocols/protocol_items.h | 55 + applications/main/subghz/protocols/raw.c | 374 ++++++ applications/main/subghz/protocols/raw.h | 148 +++ .../main/subghz/protocols/scher_khan.c | 288 +++++ .../main/subghz/protocols/scher_khan.h | 73 ++ .../main/subghz/protocols/secplus_v1.c | 634 ++++++++++ .../main/subghz/protocols/secplus_v1.h | 113 ++ .../main/subghz/protocols/secplus_v2.c | 833 ++++++++++++ .../main/subghz/protocols/secplus_v2.h | 125 ++ applications/main/subghz/protocols/smc5326.c | 387 ++++++ applications/main/subghz/protocols/smc5326.h | 107 ++ .../main/subghz/protocols/somfy_keytis.c | 797 ++++++++++++ .../main/subghz/protocols/somfy_keytis.h | 125 ++ .../main/subghz/protocols/somfy_telis.c | 663 ++++++++++ .../main/subghz/protocols/somfy_telis.h | 125 ++ .../main/subghz/protocols/star_line.c | 780 ++++++++++++ .../main/subghz/protocols/star_line.h | 131 ++ applications/main/subghz/receiver.c | 124 ++ applications/main/subghz/receiver.h | 73 ++ applications/main/subghz/registry.c | 30 + applications/main/subghz/registry.h | 47 + .../main/subghz/scenes/subghz_scene_config.h | 1 + .../subghz/scenes/subghz_scene_decode_raw.c | 5 - .../scenes/subghz_scene_delete_success.c | 1 - .../scenes/subghz_scene_ext_module_settings.c | 92 ++ .../subghz/scenes/subghz_scene_read_raw.c | 11 +- .../subghz/scenes/subghz_scene_receiver.c | 19 +- .../scenes/subghz_scene_receiver_config.c | 426 +------ .../scenes/subghz_scene_receiver_info.c | 17 +- .../scenes/subghz_scene_show_error_sub.c | 12 +- .../main/subghz/scenes/subghz_scene_start.c | 50 +- applications/main/subghz/subghz.c | 48 +- applications/main/subghz/subghz_cli.c | 115 +- .../main/subghz/subghz_file_encoder_worker.c | 244 ++++ .../main/subghz/subghz_file_encoder_worker.h | 65 + applications/main/subghz/subghz_history.c | 763 +---------- applications/main/subghz/subghz_history.h | 7 - applications/main/subghz/subghz_i.c | 44 +- applications/main/subghz/subghz_i.h | 18 +- applications/main/subghz/subghz_keystore.c | 613 +++++++++ applications/main/subghz/subghz_keystore.h | 80 ++ .../main/subghz/subghz_last_settings.c | 43 +- .../main/subghz/subghz_last_settings.h | 14 - applications/main/subghz/subghz_setting.c | 480 +++++++ applications/main/subghz/subghz_setting.h | 58 + .../main/subghz/subghz_tx_rx_worker.c | 260 ++++ .../main/subghz/subghz_tx_rx_worker.h | 89 ++ applications/main/subghz/subghz_worker.c | 149 +++ applications/main/subghz/subghz_worker.h | 80 ++ applications/main/subghz/transmitter.c | 64 + applications/main/subghz/transmitter.h | 55 + applications/main/subghz/types.h | 104 ++ applications/main/subghz/views/receiver.c | 59 +- applications/main/subghz/views/receiver.h | 2 + .../subghz/views/subghz_frequency_analyzer.c | 9 +- .../main/subghz/views/subghz_read_raw.c | 21 +- .../main/subghz/views/subghz_test_carrier.c | 12 +- .../main/subghz/views/subghz_test_static.c | 5 +- applications/main/subghz/views/transmitter.c | 9 +- firmware/targets/f7/api_symbols.csv | 681 +++++++++- .../targets/f7/furi_hal/furi_hal_resources.c | 28 + .../targets/f7/furi_hal/furi_hal_resources.h | 12 + .../targets/f7/furi_hal/furi_hal_spi_config.c | 21 +- .../targets/f7/furi_hal/furi_hal_spi_config.h | 6 +- .../targets/f7/furi_hal/furi_hal_subghz.c | 616 +++++---- .../targets/f7/furi_hal/furi_hal_subghz.h | 51 + .../f7/furi_hal/furi_hal_subghz_configs.h | 10 - lib/subghz/SConscript | 1 + lib/subghz/blocks/encoder.c | 2 +- lib/subghz/blocks/generic.c | 2 +- lib/subghz/blocks/generic.h | 2 +- lib/subghz/protocols/bin_raw.c | 4 +- lib/subghz/protocols/kinggates_stylo_4k.c | 287 ++++- lib/subghz/protocols/kinggates_stylo_4k.h | 36 + lib/subghz/protocols/protocol_items.c | 2 +- lib/subghz/protocols/protocol_items.h | 2 +- lib/subghz/protocols/raw.c | 182 +-- lib/subghz/protocols/raw.h | 58 +- lib/subghz/protocols/secplus_v2.c | 8 +- lib/subghz/receiver.c | 5 - lib/subghz/receiver.h | 7 - lib/subghz/subghz_tx_rx_worker.c | 12 +- lib/subghz/types.h | 1 + 174 files changed, 31269 insertions(+), 1885 deletions(-) create mode 100644 applications/main/subghz/SConscript create mode 100644 applications/main/subghz/blocks/const.c create mode 100644 applications/main/subghz/blocks/const.h create mode 100644 applications/main/subghz/blocks/decoder.c create mode 100644 applications/main/subghz/blocks/decoder.h create mode 100644 applications/main/subghz/blocks/encoder.c create mode 100644 applications/main/subghz/blocks/encoder.h create mode 100644 applications/main/subghz/blocks/generic.c create mode 100644 applications/main/subghz/blocks/generic.h create mode 100644 applications/main/subghz/blocks/math.c create mode 100644 applications/main/subghz/blocks/math.h create mode 100644 applications/main/subghz/environment.c create mode 100644 applications/main/subghz/environment.h create mode 100644 applications/main/subghz/protocols/alutech_at_4n.c create mode 100644 applications/main/subghz/protocols/alutech_at_4n.h create mode 100644 applications/main/subghz/protocols/ansonic.c create mode 100644 applications/main/subghz/protocols/ansonic.h create mode 100644 applications/main/subghz/protocols/base.c create mode 100644 applications/main/subghz/protocols/base.h create mode 100644 applications/main/subghz/protocols/bett.c create mode 100644 applications/main/subghz/protocols/bett.h create mode 100644 applications/main/subghz/protocols/bin_raw.c create mode 100644 applications/main/subghz/protocols/bin_raw.h create mode 100644 applications/main/subghz/protocols/came.c create mode 100644 applications/main/subghz/protocols/came.h create mode 100644 applications/main/subghz/protocols/came_atomo.c create mode 100644 applications/main/subghz/protocols/came_atomo.h create mode 100644 applications/main/subghz/protocols/came_twee.c create mode 100644 applications/main/subghz/protocols/came_twee.h create mode 100644 applications/main/subghz/protocols/chamberlain_code.c create mode 100644 applications/main/subghz/protocols/chamberlain_code.h create mode 100644 applications/main/subghz/protocols/clemsa.c create mode 100644 applications/main/subghz/protocols/clemsa.h create mode 100644 applications/main/subghz/protocols/doitrand.c create mode 100644 applications/main/subghz/protocols/doitrand.h create mode 100644 applications/main/subghz/protocols/dooya.c create mode 100644 applications/main/subghz/protocols/dooya.h create mode 100644 applications/main/subghz/protocols/faac_slh.c create mode 100644 applications/main/subghz/protocols/faac_slh.h create mode 100644 applications/main/subghz/protocols/gate_tx.c create mode 100644 applications/main/subghz/protocols/gate_tx.h create mode 100644 applications/main/subghz/protocols/holtek.c create mode 100644 applications/main/subghz/protocols/holtek.h create mode 100644 applications/main/subghz/protocols/holtek_ht12x.c create mode 100644 applications/main/subghz/protocols/holtek_ht12x.h create mode 100644 applications/main/subghz/protocols/honeywell_wdb.c create mode 100644 applications/main/subghz/protocols/honeywell_wdb.h create mode 100644 applications/main/subghz/protocols/hormann.c create mode 100644 applications/main/subghz/protocols/hormann.h create mode 100644 applications/main/subghz/protocols/ido.c create mode 100644 applications/main/subghz/protocols/ido.h create mode 100644 applications/main/subghz/protocols/intertechno_v3.c create mode 100644 applications/main/subghz/protocols/intertechno_v3.h create mode 100644 applications/main/subghz/protocols/keeloq.c create mode 100644 applications/main/subghz/protocols/keeloq.h create mode 100644 applications/main/subghz/protocols/keeloq_common.c create mode 100644 applications/main/subghz/protocols/keeloq_common.h create mode 100644 applications/main/subghz/protocols/kia.c create mode 100644 applications/main/subghz/protocols/kia.h create mode 100644 applications/main/subghz/protocols/kinggates_stylo_4k.c create mode 100644 applications/main/subghz/protocols/kinggates_stylo_4k.h create mode 100644 applications/main/subghz/protocols/linear.c create mode 100644 applications/main/subghz/protocols/linear.h create mode 100644 applications/main/subghz/protocols/linear_delta3.c create mode 100644 applications/main/subghz/protocols/linear_delta3.h create mode 100644 applications/main/subghz/protocols/magellan.c create mode 100644 applications/main/subghz/protocols/magellan.h create mode 100644 applications/main/subghz/protocols/marantec.c create mode 100644 applications/main/subghz/protocols/marantec.h create mode 100644 applications/main/subghz/protocols/megacode.c create mode 100644 applications/main/subghz/protocols/megacode.h create mode 100644 applications/main/subghz/protocols/nero_radio.c create mode 100644 applications/main/subghz/protocols/nero_radio.h create mode 100644 applications/main/subghz/protocols/nero_sketch.c create mode 100644 applications/main/subghz/protocols/nero_sketch.h create mode 100644 applications/main/subghz/protocols/nice_flo.c create mode 100644 applications/main/subghz/protocols/nice_flo.h create mode 100644 applications/main/subghz/protocols/nice_flor_s.c create mode 100644 applications/main/subghz/protocols/nice_flor_s.h create mode 100644 applications/main/subghz/protocols/phoenix_v2.c create mode 100644 applications/main/subghz/protocols/phoenix_v2.h create mode 100644 applications/main/subghz/protocols/power_smart.c create mode 100644 applications/main/subghz/protocols/power_smart.h create mode 100644 applications/main/subghz/protocols/princeton.c create mode 100644 applications/main/subghz/protocols/princeton.h create mode 100644 applications/main/subghz/protocols/princeton_for_testing.c create mode 100644 applications/main/subghz/protocols/princeton_for_testing.h create mode 100644 applications/main/subghz/protocols/protocol_items.c create mode 100644 applications/main/subghz/protocols/protocol_items.h create mode 100644 applications/main/subghz/protocols/raw.c create mode 100644 applications/main/subghz/protocols/raw.h create mode 100644 applications/main/subghz/protocols/scher_khan.c create mode 100644 applications/main/subghz/protocols/scher_khan.h create mode 100644 applications/main/subghz/protocols/secplus_v1.c create mode 100644 applications/main/subghz/protocols/secplus_v1.h create mode 100644 applications/main/subghz/protocols/secplus_v2.c create mode 100644 applications/main/subghz/protocols/secplus_v2.h create mode 100644 applications/main/subghz/protocols/smc5326.c create mode 100644 applications/main/subghz/protocols/smc5326.h create mode 100644 applications/main/subghz/protocols/somfy_keytis.c create mode 100644 applications/main/subghz/protocols/somfy_keytis.h create mode 100644 applications/main/subghz/protocols/somfy_telis.c create mode 100644 applications/main/subghz/protocols/somfy_telis.h create mode 100644 applications/main/subghz/protocols/star_line.c create mode 100644 applications/main/subghz/protocols/star_line.h create mode 100644 applications/main/subghz/receiver.c create mode 100644 applications/main/subghz/receiver.h create mode 100644 applications/main/subghz/registry.c create mode 100644 applications/main/subghz/registry.h create mode 100644 applications/main/subghz/scenes/subghz_scene_ext_module_settings.c create mode 100644 applications/main/subghz/subghz_file_encoder_worker.c create mode 100644 applications/main/subghz/subghz_file_encoder_worker.h create mode 100644 applications/main/subghz/subghz_keystore.c create mode 100644 applications/main/subghz/subghz_keystore.h create mode 100644 applications/main/subghz/subghz_setting.c create mode 100644 applications/main/subghz/subghz_setting.h create mode 100644 applications/main/subghz/subghz_tx_rx_worker.c create mode 100644 applications/main/subghz/subghz_tx_rx_worker.h create mode 100644 applications/main/subghz/subghz_worker.c create mode 100644 applications/main/subghz/subghz_worker.h create mode 100644 applications/main/subghz/transmitter.c create mode 100644 applications/main/subghz/transmitter.h create mode 100644 applications/main/subghz/types.h diff --git a/applications/main/subghz/SConscript b/applications/main/subghz/SConscript new file mode 100644 index 000000000..8fbec94ad --- /dev/null +++ b/applications/main/subghz/SConscript @@ -0,0 +1,32 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/subghz", + ], + SDK_HEADERS=[ + File("environment.h"), + File("receiver.h"), + File("subghz_worker.h"), + File("subghz_tx_rx_worker.h"), + File("transmitter.h"), + File("registry.h"), + File("protocols/protocol_items.h"), + File("protocols/raw.h"), + File("blocks/const.h"), + File("blocks/decoder.h"), + File("blocks/encoder.h"), + File("blocks/generic.h"), + File("blocks/math.h"), + File("subghz_setting.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="subghz") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index 6df4b1a8a..f0dc66e89 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -12,7 +12,7 @@ App( ], provides=["subghz_start"], icon="A_Sub1ghz_14", - stack_size=2 * 1024, + stack_size=3 * 1024, order=10, ) diff --git a/applications/main/subghz/blocks/const.c b/applications/main/subghz/blocks/const.c new file mode 100644 index 000000000..15719b2ac --- /dev/null +++ b/applications/main/subghz/blocks/const.c @@ -0,0 +1 @@ +#include "const.h" diff --git a/applications/main/subghz/blocks/const.h b/applications/main/subghz/blocks/const.h new file mode 100644 index 000000000..f32334e2f --- /dev/null +++ b/applications/main/subghz/blocks/const.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const uint16_t te_long; + const uint16_t te_short; + const uint16_t te_delta; + const uint8_t min_count_bit_for_found; +} SubGhzBlockConst; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/decoder.c b/applications/main/subghz/blocks/decoder.c new file mode 100644 index 000000000..f491c87bf --- /dev/null +++ b/applications/main/subghz/blocks/decoder.c @@ -0,0 +1,27 @@ +#include "decoder.h" + +#define TAG "SubGhzBlockDecoder" + +void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit) { + decoder->decode_data = decoder->decode_data << 1 | bit; + decoder->decode_count_bit++; +} + +void subghz_protocol_blocks_add_to_128_bit( + SubGhzBlockDecoder* decoder, + uint8_t bit, + uint64_t* head_64_bit) { + if(++decoder->decode_count_bit > 64) { + (*head_64_bit) = ((*head_64_bit) << 1) | (decoder->decode_data >> 63); + } + decoder->decode_data = decoder->decode_data << 1 | bit; +} + +uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len) { + uint8_t hash = 0; + uint8_t* p = (uint8_t*)&decoder->decode_data; + for(size_t i = 0; i < len; i++) { + hash ^= p[i]; + } + return hash; +} diff --git a/applications/main/subghz/blocks/decoder.h b/applications/main/subghz/blocks/decoder.h new file mode 100644 index 000000000..a5e561e35 --- /dev/null +++ b/applications/main/subghz/blocks/decoder.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzBlockDecoder SubGhzBlockDecoder; + +struct SubGhzBlockDecoder { + uint32_t parser_step; + uint32_t te_last; + uint64_t decode_data; + uint8_t decode_count_bit; +}; + +/** + * Add data bit when decoding. + * @param decoder Pointer to a SubGhzBlockDecoder instance + * @param bit data, 1bit + */ +void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit); + +/** + * Add data to_128 bit when decoding. + * @param decoder Pointer to a SubGhzBlockDecoder instance + * @param head_64_bit Pointer to a head_64_bit + * @param bit data, 1bit + */ +void subghz_protocol_blocks_add_to_128_bit( + SubGhzBlockDecoder* decoder, + uint8_t bit, + uint64_t* head_64_bit); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param decoder Pointer to a SubGhzBlockDecoder instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/encoder.c b/applications/main/subghz/blocks/encoder.c new file mode 100644 index 000000000..49ec4f177 --- /dev/null +++ b/applications/main/subghz/blocks/encoder.c @@ -0,0 +1,58 @@ +#include "encoder.h" +#include "math.h" +#include + +#include "furi.h" + +#define TAG "SubGhzBlockEncoder" + +void subghz_protocol_blocks_set_bit_array( + bool bit_value, + uint8_t data_array[], + size_t set_index_bit, + size_t max_size_array) { + furi_assert(set_index_bit < max_size_array * 8); + bit_write(data_array[set_index_bit >> 3], 7 - (set_index_bit & 0x7), bit_value); +} + +bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_index_bit) { + return bit_read(data_array[read_index_bit >> 3], 7 - (read_index_bit & 0x7)); +} + +size_t subghz_protocol_blocks_get_upload_from_bit_array( + uint8_t data_array[], + size_t count_bit_data_array, + LevelDuration* upload, + size_t max_size_upload, + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit) { + size_t bias_bit = 0; + size_t size_upload = 0; + uint32_t duration = duration_bit; + + if(align_bit == SubGhzProtocolBlockAlignBitRight) { + if(count_bit_data_array & 0x7) { + bias_bit = 8 - (count_bit_data_array & 0x7); + } + } + size_t index_bit = bias_bit; + + bool last_bit = subghz_protocol_blocks_get_bit_array(data_array, index_bit++); + for(size_t i = 1 + bias_bit; i < count_bit_data_array + bias_bit; i++) { + if(last_bit == subghz_protocol_blocks_get_bit_array(data_array, index_bit)) { + duration += duration_bit; + } else { + if(size_upload > max_size_upload) { + furi_crash("SubGhz: Encoder buffer overflow"); + } + upload[size_upload++] = level_duration_make( + subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); + last_bit = !last_bit; + duration = duration_bit; + } + index_bit++; + } + upload[size_upload++] = level_duration_make( + subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); + return size_upload; +} diff --git a/applications/main/subghz/blocks/encoder.h b/applications/main/subghz/blocks/encoder.h new file mode 100644 index 000000000..aeaa2add0 --- /dev/null +++ b/applications/main/subghz/blocks/encoder.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + bool is_running; + size_t repeat; + size_t front; + size_t size_upload; + LevelDuration* upload; + +} SubGhzProtocolBlockEncoder; + +typedef enum { + SubGhzProtocolBlockAlignBitLeft, + SubGhzProtocolBlockAlignBitRight, +} SubGhzProtocolBlockAlignBit; + +/** + * Set data bit when encoding HEX array. + * @param bit_value The value of the bit to be set + * @param data_array Pointer to a HEX array + * @param set_index_bit Number set a bit in the array starting from the left + * @param max_size_array array size, check not to overflow + */ +void subghz_protocol_blocks_set_bit_array( + bool bit_value, + uint8_t data_array[], + size_t set_index_bit, + size_t max_size_array); + +/** + * Get data bit when encoding HEX array. + * @param data_array Pointer to a HEX array + * @param read_index_bit Number get a bit in the array starting from the left + * @return bool value bit + */ +bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_index_bit); + +/** + * Generating an upload from data. + * @param data_array Pointer to a HEX array + * @param count_bit_data_array How many bits in the array are processed + * @param upload Pointer to a LevelDuration + * @param max_size_upload upload size, check not to overflow + * @param duration_bit duration 1 bit + * @param align_bit alignment of useful bits in an array + */ +size_t subghz_protocol_blocks_get_upload_from_bit_array( + uint8_t data_array[], + size_t count_bit_data_array, + LevelDuration* upload, + size_t max_size_upload, + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/generic.c b/applications/main/subghz/blocks/generic.c new file mode 100644 index 000000000..3d59adc82 --- /dev/null +++ b/applications/main/subghz/blocks/generic.c @@ -0,0 +1,120 @@ +#include "generic.h" +#include +#include + +#define TAG "SubGhzBlockGeneric" + +void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str) { + const char* preset_name_temp; + if(!strcmp(preset_name, "AM270")) { + preset_name_temp = "FuriHalSubGhzPresetOok270Async"; + } else if(!strcmp(preset_name, "AM650")) { + preset_name_temp = "FuriHalSubGhzPresetOok650Async"; + } else if(!strcmp(preset_name, "FM238")) { + preset_name_temp = "FuriHalSubGhzPreset2FSKDev238Async"; + } else if(!strcmp(preset_name, "FM476")) { + preset_name_temp = "FuriHalSubGhzPreset2FSKDev476Async"; + } else { + preset_name_temp = "FuriHalSubGhzPresetCustom"; + } + furi_string_set(preset_str, preset_name_temp); +} + +bool subghz_block_generic_serialize( + SubGhzBlockGeneric* instance, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(instance); + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_header_cstr( + flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + flipper_format, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + flipper_format, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + uint32_t temp = instance->data_count_bit; + if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Bit"); + break; + } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; + } + + if(!flipper_format_write_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + res = true; + } while(false); + furi_string_free(temp_str); + return res; +} + +bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format) { + furi_assert(instance); + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + instance->data_count_bit = (uint16_t)temp_data; + + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Key"); + break; + } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->data = instance->data << 8 | key_data[i]; + } + + res = true; + } while(0); + + furi_string_free(temp_str); + + return res; +} diff --git a/applications/main/subghz/blocks/generic.h b/applications/main/subghz/blocks/generic.h new file mode 100644 index 000000000..e69de8b4f --- /dev/null +++ b/applications/main/subghz/blocks/generic.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include "../types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzBlockGeneric SubGhzBlockGeneric; + +struct SubGhzBlockGeneric { + const char* protocol_name; + uint64_t data; + uint64_t data_2; + uint32_t serial; + uint16_t data_count_bit; + uint8_t btn; + uint32_t cnt; + uint8_t cnt_2; + uint32_t seed; +}; + +/** + * Get name preset. + * @param preset_name name preset + * @param preset_str Output name preset + */ +void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str); + +/** + * Serialize data SubGhzBlockGeneric. + * @param instance Pointer to a SubGhzBlockGeneric instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_block_generic_serialize( + SubGhzBlockGeneric* instance, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzBlockGeneric. + * @param instance Pointer to a SubGhzBlockGeneric instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/math.c b/applications/main/subghz/blocks/math.c new file mode 100644 index 000000000..24202ad1c --- /dev/null +++ b/applications/main/subghz/blocks/math.c @@ -0,0 +1,244 @@ +#include "math.h" + +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) { + uint64_t reverse_key = 0; + for(uint8_t i = 0; i < bit_count; i++) { + reverse_key = reverse_key << 1 | bit_read(key, i); + } + return reverse_key; +} + +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) { + uint8_t parity = 0; + for(uint8_t i = 0; i < bit_count; i++) { + parity += bit_read(key, i); + } + return parity & 0x01; +} + +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 4; // LSBs are unused + uint8_t poly = polynomial << 4; + uint8_t bit; + + while(size--) { + remainder ^= *message++; + for(bit = 0; bit < 8; bit++) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 4 & 0x0f; // discard the LSBs +} + +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 1; // LSB is unused + uint8_t poly = polynomial << 1; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 1 & 0x7f; // discard the LSB +} + +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8); + polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8); + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte] << 8; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x8000) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int i = 7; i >= 0; --i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + // Process message from last byte to first byte (reflected) + for(int byte = size - 1; byte >= 0; --byte) { + uint8_t data = message[byte]; + // Process individual bits of each byte (reflected) + for(uint8_t i = 0; i < 8; ++i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) { + sum ^= key; + } + + // roll the key left (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped lsb as MSB) + if(key & 0x80) + key = (key << 1) ^ gen; + else + key = (key << 1); + } + } + return sum; +} + +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + size_t size, + uint16_t gen, + uint16_t key) { + uint16_t sum = 0; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int8_t i = 7; i >= 0; --i) { + // if data bit is set then xor with key + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) { + uint32_t result = 0; + for(size_t i = 0; i < size; ++i) { + result += message[i]; + } + return (uint8_t)result; +} + +uint8_t subghz_protocol_blocks_parity8(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; +} + +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { + result ^= subghz_protocol_blocks_parity8(message[i]); + } + return result; +} + +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { + result ^= message[i]; + } + return result; +} \ No newline at end of file diff --git a/applications/main/subghz/blocks/math.h b/applications/main/subghz/blocks/math.h new file mode 100644 index 000000000..dcea3da5f --- /dev/null +++ b/applications/main/subghz/blocks/math.h @@ -0,0 +1,222 @@ +#pragma once + +#include +#include +#include + +#define bit_read(value, bit) (((value) >> (bit)) & 0x01) +#define bit_set(value, bit) \ + ({ \ + __typeof__(value) _one = (1); \ + (value) |= (_one << (bit)); \ + }) +#define bit_clear(value, bit) \ + ({ \ + __typeof__(value) _one = (1); \ + (value) &= ~(_one << (bit)); \ + }) +#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) +#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) + +#ifdef __cplusplus +extern "C" { +#endif + +/** Flip the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return Reverse data + */ +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count); + +/** Get parity the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return parity + */ +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count); + +/** CRC-4 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** CRC-7 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 + + * x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal + * bit-by-bit parity XOR) + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** "Little-endian" Cyclic Redundancy Check CRC-8 LE Input and output are + * reflected, i.e. least significant bit is shifted in first + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is + * shifted in first. Note that poly and init already need to be reflected + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** CRC-16 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** Digest-8 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key); + +/** Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key); + +/** Digest-16 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + size_t size, + uint16_t gen, + uint16_t key); + +/** Compute Addition of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value + */ +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size); + +/** Compute bit parity of a single byte (8 bits) + * + * @param byte single byte to check + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity8(uint8_t byte); + +/** Compute bit parity of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size); + +/** Compute XOR (byte-wide parity) of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value, per bit-position 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/environment.c b/applications/main/subghz/environment.c new file mode 100644 index 000000000..b39b259d4 --- /dev/null +++ b/applications/main/subghz/environment.c @@ -0,0 +1,116 @@ +#include "environment.h" +#include "registry.h" + +struct SubGhzEnvironment { + SubGhzKeystore* keystore; + const SubGhzProtocolRegistry* protocol_registry; + const char* came_atomo_rainbow_table_file_name; + const char* nice_flor_s_rainbow_table_file_name; + const char* alutech_at_4n_rainbow_table_file_name; +}; + +SubGhzEnvironment* subghz_environment_alloc() { + SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment)); + + instance->keystore = subghz_keystore_alloc(); + instance->protocol_registry = NULL; + instance->came_atomo_rainbow_table_file_name = NULL; + instance->nice_flor_s_rainbow_table_file_name = NULL; + + return instance; +} + +void subghz_environment_free(SubGhzEnvironment* instance) { + furi_assert(instance); + + instance->protocol_registry = NULL; + instance->came_atomo_rainbow_table_file_name = NULL; + instance->nice_flor_s_rainbow_table_file_name = NULL; + subghz_keystore_free(instance->keystore); + + free(instance); +} + +bool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename) { + furi_assert(instance); + + return subghz_keystore_load(instance->keystore, filename); +} + +SubGhzKeystore* subghz_environment_get_keystore(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->keystore; +} + +void subghz_environment_set_came_atomo_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename) { + furi_assert(instance); + + instance->came_atomo_rainbow_table_file_name = filename; +} + +const char* + subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->came_atomo_rainbow_table_file_name; +} + +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename) { + furi_assert(instance); + + instance->alutech_at_4n_rainbow_table_file_name = filename; +} + +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->alutech_at_4n_rainbow_table_file_name; +} + +void subghz_environment_set_nice_flor_s_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename) { + furi_assert(instance); + + instance->nice_flor_s_rainbow_table_file_name = filename; +} + +const char* + subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->nice_flor_s_rainbow_table_file_name; +} + +void subghz_environment_set_protocol_registry( + SubGhzEnvironment* instance, + void* protocol_registry_items) { + furi_assert(instance); + const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items; + instance->protocol_registry = protocol_registry; +} + +void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { + furi_assert(instance); + furi_assert(instance->protocol_registry); + return (void*)instance->protocol_registry; +} + +const char* + subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx) { + furi_assert(instance); + furi_assert(instance->protocol_registry); + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_index(instance->protocol_registry, idx); + if(protocol != NULL) { + return protocol->name; + } else { + return NULL; + } +} \ No newline at end of file diff --git a/applications/main/subghz/environment.h b/applications/main/subghz/environment.h new file mode 100644 index 000000000..7bd38ba2f --- /dev/null +++ b/applications/main/subghz/environment.h @@ -0,0 +1,115 @@ +#pragma once + +#include + +#include "subghz_keystore.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzEnvironment SubGhzEnvironment; + +/** + * Allocate SubGhzEnvironment. + * @return SubGhzEnvironment* pointer to a SubGhzEnvironment instance + */ +SubGhzEnvironment* subghz_environment_alloc(); + +/** + * Free SubGhzEnvironment. + * @param instance Pointer to a SubGhzEnvironment instance + */ +void subghz_environment_free(SubGhzEnvironment* instance); + +/** + * Downloading the manufacture key file. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + * @return true On success + */ +bool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename); + +/** + * Get pointer to a SubGhzKeystore* instance. + * @return SubGhzEnvironment* pointer to a SubGhzEnvironment instance + */ +SubGhzKeystore* subghz_environment_get_keystore(SubGhzEnvironment* instance); + +/** + * Set filename to work with Came Atomo. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + */ +void subghz_environment_set_came_atomo_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename); + +/** + * Get filename to work with Came Atomo. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Full path to the file + */ +const char* subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance); + +/** + * Set filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + */ +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename); + +/** + * Get filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Full path to the file + */ +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance); + +/** + * Set filename to work with Nice Flor-S. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + */ +void subghz_environment_set_nice_flor_s_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename); + +/** + * Get filename to work with Nice Flor-S. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Full path to the file + */ +const char* + subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance); + +/** + * Set list of protocols to work. + * @param instance Pointer to a SubGhzEnvironment instance + * @param protocol_registry_items Pointer to a SubGhzProtocolRegistry + */ +void subghz_environment_set_protocol_registry( + SubGhzEnvironment* instance, + void* protocol_registry_items); + +/** + * Get list of protocols to work. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Pointer to a SubGhzProtocolRegistry + */ +void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); + +/** + * Get list of protocols names. + * @param instance Pointer to a SubGhzEnvironment instance + * @param idx index protocols + * @return Pointer to a SubGhzProtocolRegistry + */ +const char* subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 0559ac67e..350e68ee6 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -50,7 +50,6 @@ typedef enum { SubGhzCustomEventSceneAnalyzerLock, SubGhzCustomEventSceneAnalyzerUnlock, SubGhzCustomEventSceneSettingLock, - SubGhzCustomEventSceneSettingError, SubGhzCustomEventSceneExit, SubGhzCustomEventSceneStay, diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 7dc67c9f2..6452792a6 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -36,13 +36,13 @@ struct SubGhzFrequencyAnalyzerWorker { }; static void subghz_frequency_analyzer_worker_load_registers(const uint8_t data[][2]) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); size_t i = 0; while(data[i][0]) { - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i][0], data[i][1]); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, data[i][0], data[i][1]); i++; } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } // running average with adaptive coefficient @@ -80,26 +80,26 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { //Start CC1101 furi_hal_subghz_reset(); - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_rx(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_MDMCFG3, + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_flush_rx(furi_hal_subghz.spi_bus_handle); + cc1101_flush_tx(furi_hal_subghz.spi_bus_handle); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_MDMCFG3, 0b01111111); // symbol rate cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, + furi_hal_subghz.spi_bus_handle, CC1101_AGCCTRL2, 0b00000111); // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAGN_TARGET 42 dB cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, + furi_hal_subghz.spi_bus_handle, CC1101_AGCCTRL1, 0b00001000); // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 1000 - Absolute carrier sense threshold disabled cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, + furi_hal_subghz.spi_bus_handle, CC1101_AGCCTRL0, 0b00110000); // 00 - No hysteresis, medium asymmetric dead zone, medium gain ; 11 - 64 samples agc; 00 - Normal AGC, 00 - 4dB boundary - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); @@ -118,20 +118,23 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { // First stage: coarse scan for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) { if(furi_hal_subghz_is_frequency_valid( - subghz_setting_get_frequency(instance->setting, i))) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + subghz_setting_get_frequency(instance->setting, i)) && + !((furi_hal_subghz.radio_type == SubGhzRadioExternal) && + (subghz_setting_get_frequency(instance->setting, i) >= 311900000 && + subghz_setting_get_frequency(instance->setting, i) <= 312200000))) { + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle); frequency = cc1101_set_frequency( - &furi_hal_spi_bus_handle_subghz, + furi_hal_subghz.spi_bus_handle, subghz_setting_get_frequency(instance->setting, i)); - cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); + cc1101_calibrate(furi_hal_subghz.spi_bus_handle); do { - status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); + status = cc1101_get_status(furi_hal_subghz.spi_bus_handle); } while(status.STATE != CC1101StateIDLE); - cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_switch_to_rx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); furi_delay_ms(2); @@ -166,17 +169,17 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { i < frequency_rssi.frequency_coarse + 300000; i += 20000) { if(furi_hal_subghz_is_frequency_valid(i)) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); - frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, i); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle); + frequency = cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, i); - cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); + cc1101_calibrate(furi_hal_subghz.spi_bus_handle); do { - status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); + status = cc1101_get_status(furi_hal_subghz.spi_bus_handle); } while(status.STATE != CC1101StateIDLE); - cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_switch_to_rx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); furi_delay_ms(2); diff --git a/applications/main/subghz/protocols/alutech_at_4n.c b/applications/main/subghz/protocols/alutech_at_4n.c new file mode 100644 index 000000000..6bcf9f25d --- /dev/null +++ b/applications/main/subghz/protocols/alutech_at_4n.c @@ -0,0 +1,455 @@ +#include "alutech_at_4n.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoAlutech_at_4n" + +#define SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE 0xFFFFFFFF + +static const SubGhzBlockConst subghz_protocol_alutech_at_4n_const = { + .te_short = 400, + .te_long = 800, + .te_delta = 140, + .min_count_bit_for_found = 72, +}; + +struct SubGhzProtocolDecoderAlutech_at_4n { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint32_t crc; + uint16_t header_count; + + const char* alutech_at_4n_rainbow_table_file_name; +}; + +struct SubGhzProtocolEncoderAlutech_at_4n { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Alutech_at_4nDecoderStepReset = 0, + Alutech_at_4nDecoderStepCheckPreambula, + Alutech_at_4nDecoderStepSaveDuration, + Alutech_at_4nDecoderStepCheckDuration, +} Alutech_at_4nDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = { + .alloc = subghz_protocol_decoder_alutech_at_4n_alloc, + .free = subghz_protocol_decoder_alutech_at_4n_free, + + .feed = subghz_protocol_decoder_alutech_at_4n_feed, + .reset = subghz_protocol_decoder_alutech_at_4n_reset, + + .get_hash_data = subghz_protocol_decoder_alutech_at_4n_get_hash_data, + .serialize = subghz_protocol_decoder_alutech_at_4n_serialize, + .deserialize = subghz_protocol_decoder_alutech_at_4n_deserialize, + .get_string = subghz_protocol_decoder_alutech_at_4n_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_alutech_at_4n = { + .name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_alutech_at_4n_decoder, + .encoder = &subghz_protocol_alutech_at_4n_encoder, +}; + +/** + * Read bytes from rainbow table + * @param file_name Full path to rainbow table the file + * @param number_alutech_at_4n_magic_data number in the array + * @return alutech_at_4n_magic_data + */ +static uint32_t subghz_protocol_alutech_at_4n_get_magic_data_in_file( + const char* file_name, + uint8_t number_alutech_at_4n_magic_data) { + if(!strcmp(file_name, "")) return SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + + uint8_t buffer[sizeof(uint32_t)] = {0}; + uint32_t address = number_alutech_at_4n_magic_data * sizeof(uint32_t); + uint32_t alutech_at_4n_magic_data = 0; + + if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint32_t))) { + for(size_t i = 0; i < sizeof(uint32_t); i++) { + alutech_at_4n_magic_data = (alutech_at_4n_magic_data << 8) | buffer[i]; + } + } else { + alutech_at_4n_magic_data = SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + } + return alutech_at_4n_magic_data; +} + +static uint8_t subghz_protocol_alutech_at_4n_crc(uint64_t data) { + uint8_t* p = (uint8_t*)&data; + uint8_t crc = 0xff; + for(uint8_t y = 0; y < 8; y++) { + crc = crc ^ p[y]; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + } + return crc; +} + +static uint8_t subghz_protocol_alutech_at_4n_decrypt_data_crc(uint8_t data) { + uint8_t crc = data; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + return ~crc; +} + +static uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* file_name) { + uint8_t* p = (uint8_t*)&data; + uint32_t data1 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + uint32_t data2 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; + uint32_t data3 = 0; + uint32_t magic_data[] = { + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 3), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5)}; + + uint32_t i = magic_data[0]; + do { + data2 = data2 - + ((magic_data[1] + (data1 << 4)) ^ ((magic_data[2] + (data1 >> 5)) ^ (data1 + i))); + data3 = data2 + i; + i += magic_data[3]; + data1 = + data1 - ((magic_data[4] + (data2 << 4)) ^ ((magic_data[5] + (data2 >> 5)) ^ data3)); + } while(i != 0); + + p[0] = (uint8_t)(data1 >> 24); + p[1] = (uint8_t)(data1 >> 16); + p[3] = (uint8_t)data1; + p[4] = (uint8_t)(data2 >> 24); + p[5] = (uint8_t)(data2 >> 16); + p[2] = (uint8_t)(data1 >> 8); + p[6] = (uint8_t)(data2 >> 8); + p[7] = (uint8_t)data2; + + return data; +} + +// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { +// uint8_t* p = (uint8_t*)&data; +// uint32_t data1 = 0; +// uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +// uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; +// uint32_t magic_data[] = { +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; + +// do { +// data1 = data1 + magic_data[0]; +// data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ +// ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); +// data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ +// ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); +// } while(data1 != magic_data[5]); +// p[0] = (uint8_t)(data2 >> 24); +// p[1] = (uint8_t)(data2 >> 16); +// p[3] = (uint8_t)data2; +// p[4] = (uint8_t)(data3 >> 24); +// p[5] = (uint8_t)(data3 >> 16); +// p[2] = (uint8_t)(data2 >> 8); +// p[6] = (uint8_t)(data3 >> 8); +// p[7] = (uint8_t)data3; + +// return data; +// } + +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderAlutech_at_4n* instance = + malloc(sizeof(SubGhzProtocolDecoderAlutech_at_4n)); + instance->base.protocol = &subghz_protocol_alutech_at_4n; + instance->generic.protocol_name = instance->base.protocol->name; + instance->alutech_at_4n_rainbow_table_file_name = + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment); + if(instance->alutech_at_4n_rainbow_table_file_name) { + FURI_LOG_I( + TAG, "Loading rainbow table from %s", instance->alutech_at_4n_rainbow_table_file_name); + } + return instance; +} + +void subghz_protocol_decoder_alutech_at_4n_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->alutech_at_4n_rainbow_table_file_name = NULL; + free(instance); +} + +void subghz_protocol_decoder_alutech_at_4n_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; +} + +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + + switch(instance->decoder.parser_step) { + case Alutech_at_4nDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case Alutech_at_4nDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short * 10) < + subghz_protocol_alutech_at_4n_const.te_delta * 10)) { + // Found header + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + case Alutech_at_4nDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckDuration; + } + break; + case Alutech_at_4nDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 2 + + subghz_protocol_alutech_at_4n_const.te_delta)) { + //add last bit + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + + // Found end TX + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + if(instance->generic.data != instance->data) { + instance->generic.data = instance->data; + + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->crc = instance->decoder.decode_data; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_alutech_at_4n_remote_controller( + SubGhzBlockGeneric* instance, + uint8_t crc, + const char* file_name) { + /** + * Message format 72bit LSB first + * data crc + * XXXXXXXXXXXXXXXX CC + * + * For analysis, you need to turn the package MSB + * in decoded messages format + * + * crc1 serial cnt key + * cc SSSSSSSS XXxx BB + * + * crc1 is calculated from the lower part of cnt + * key 1=0xff, 2=0x11, 3=0x22, 4=0x33, 5=0x44 + * + */ + + bool status = false; + uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 64); + crc = subghz_protocol_blocks_reverse_key(crc, 8); + + if(crc == subghz_protocol_alutech_at_4n_crc(data)) { + data = subghz_protocol_alutech_at_4n_decrypt(data, file_name); + status = true; + } + + if(status && ((uint8_t)(data >> 56) == + subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF)))) { + instance->btn = (uint8_t)data & 0xFF; + instance->cnt = (uint16_t)(data >> 8) & 0xFFFF; + instance->serial = (uint32_t)(data >> 24) & 0xFFFFFFFF; + } + + if(!status) { + instance->btn = 0; + instance->cnt = 0; + instance->serial = 0; + } +} + +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + return (uint8_t)instance->crc; +} + +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "CRC", &instance->crc, 1)) { + FURI_LOG_E(TAG, "Unable to add CRC"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "CRC", (uint32_t*)&instance->crc, 1)) { + FURI_LOG_E(TAG, "Missing CRC"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + subghz_protocol_alutech_at_4n_remote_controller( + &instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %d\r\n" + "Key:0x%08lX%08lX%02X\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%03lX\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + (uint8_t)instance->crc, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/applications/main/subghz/protocols/alutech_at_4n.h b/applications/main/subghz/protocols/alutech_at_4n.h new file mode 100644 index 000000000..38bac3ea6 --- /dev/null +++ b/applications/main/subghz/protocols/alutech_at_4n.h @@ -0,0 +1,74 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME "Alutech at-4n" + +typedef struct SubGhzProtocolDecoderAlutech_at_4n SubGhzProtocolDecoderAlutech_at_4n; +typedef struct SubGhzProtocolEncoderAlutech_at_4n SubGhzProtocolEncoderAlutech_at_4n; + +extern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder; +extern const SubGhzProtocol subghz_protocol_alutech_at_4n; + +/** + * Allocate SubGhzProtocolDecoderAlutech_at_4n. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAlutech_at_4n* pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param output Resulting text + */ +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/ansonic.c b/applications/main/subghz/protocols/ansonic.c new file mode 100644 index 000000000..81b196e36 --- /dev/null +++ b/applications/main/subghz/protocols/ansonic.c @@ -0,0 +1,346 @@ +#include "ansonic.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolAnsonic" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \ + (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \ + (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_ansonic_const = { + .te_short = 555, + .te_long = 1111, + .te_delta = 120, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderAnsonic { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderAnsonic { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + AnsonicDecoderStepReset = 0, + AnsonicDecoderStepFoundStartBit, + AnsonicDecoderStepSaveDuration, + AnsonicDecoderStepCheckDuration, +} AnsonicDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder = { + .alloc = subghz_protocol_decoder_ansonic_alloc, + .free = subghz_protocol_decoder_ansonic_free, + + .feed = subghz_protocol_decoder_ansonic_feed, + .reset = subghz_protocol_decoder_ansonic_reset, + + .get_hash_data = subghz_protocol_decoder_ansonic_get_hash_data, + .serialize = subghz_protocol_decoder_ansonic_serialize, + .deserialize = subghz_protocol_decoder_ansonic_deserialize, + .get_string = subghz_protocol_decoder_ansonic_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder = { + .alloc = subghz_protocol_encoder_ansonic_alloc, + .free = subghz_protocol_encoder_ansonic_free, + + .deserialize = subghz_protocol_encoder_ansonic_deserialize, + .stop = subghz_protocol_encoder_ansonic_stop, + .yield = subghz_protocol_encoder_ansonic_yield, +}; + +const SubGhzProtocol subghz_protocol_ansonic = { + .name = SUBGHZ_PROTOCOL_ANSONIC_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_FM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_ansonic_decoder, + .encoder = &subghz_protocol_ansonic_encoder, +}; + +void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderAnsonic* instance = malloc(sizeof(SubGhzProtocolEncoderAnsonic)); + + instance->base.protocol = &subghz_protocol_ansonic; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_ansonic_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderAnsonic* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderAnsonic instance + * @return true On success + */ +static bool subghz_protocol_encoder_ansonic_get_upload(SubGhzProtocolEncoderAnsonic* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short * 35); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); + } + } + return true; +} + +bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderAnsonic* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_ansonic_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_ansonic_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_ansonic_stop(void* context) { + SubGhzProtocolEncoderAnsonic* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_ansonic_yield(void* context) { + SubGhzProtocolEncoderAnsonic* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderAnsonic* instance = malloc(sizeof(SubGhzProtocolDecoderAnsonic)); + instance->base.protocol = &subghz_protocol_ansonic; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_ansonic_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + free(instance); +} + +void subghz_protocol_decoder_ansonic_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + instance->decoder.parser_step = AnsonicDecoderStepReset; +} + +void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + + switch(instance->decoder.parser_step) { + case AnsonicDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short * 35) < + subghz_protocol_ansonic_const.te_delta * 35)) { + //Found header Ansonic + instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; + } + break; + case AnsonicDecoderStepFoundStartBit: + if(!level) { + break; + } else if( + DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta) { + //Found start bit Ansonic + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_ansonic_const.te_short * 4)) { + instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_ansonic_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = AnsonicDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_long) < + subghz_protocol_ansonic_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_long) < + subghz_protocol_ansonic_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + } else + instance->decoder.parser_step = AnsonicDecoderStepReset; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_ansonic_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * 12345678(10) k 9 + * AAA => 10101010 1 01 0 + * + * 1...10 - DIP + * k- KEY + */ + instance->cnt = instance->data & 0xFFF; + instance->btn = ((instance->data >> 1) & 0x3); +} + +uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_ansonic_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_ansonic_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + subghz_protocol_ansonic_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%03lX\r\n" + "Btn:%X\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.btn, + CNT_TO_DIP(instance->generic.cnt)); +} diff --git a/applications/main/subghz/protocols/ansonic.h b/applications/main/subghz/protocols/ansonic.h new file mode 100644 index 000000000..0170a6048 --- /dev/null +++ b/applications/main/subghz/protocols/ansonic.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_ANSONIC_NAME "Ansonic" + +typedef struct SubGhzProtocolDecoderAnsonic SubGhzProtocolDecoderAnsonic; +typedef struct SubGhzProtocolEncoderAnsonic SubGhzProtocolEncoderAnsonic; + +extern const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder; +extern const SubGhzProtocol subghz_protocol_ansonic; + +/** + * Allocate SubGhzProtocolEncoderAnsonic. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderAnsonic* pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderAnsonic. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void subghz_protocol_encoder_ansonic_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void subghz_protocol_encoder_ansonic_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_ansonic_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderAnsonic. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAnsonic* pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void subghz_protocol_decoder_ansonic_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void subghz_protocol_decoder_ansonic_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_ansonic_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param output Resulting text + */ +void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/base.c b/applications/main/subghz/protocols/base.c new file mode 100644 index 000000000..36f33b9a5 --- /dev/null +++ b/applications/main/subghz/protocols/base.c @@ -0,0 +1,62 @@ +#include "base.h" +#include "registry.h" + +void subghz_protocol_decoder_base_set_decoder_callback( + SubGhzProtocolDecoderBase* decoder_base, + SubGhzProtocolDecoderBaseRxCallback callback, + void* context) { + decoder_base->callback = callback; + decoder_base->context = context; +} + +bool subghz_protocol_decoder_base_get_string( + SubGhzProtocolDecoderBase* decoder_base, + FuriString* output) { + bool status = false; + + if(decoder_base->protocol && decoder_base->protocol->decoder && + decoder_base->protocol->decoder->get_string) { + decoder_base->protocol->decoder->get_string(decoder_base, output); + status = true; + } + + return status; +} + +bool subghz_protocol_decoder_base_serialize( + SubGhzProtocolDecoderBase* decoder_base, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + bool status = false; + + if(decoder_base->protocol && decoder_base->protocol->decoder && + decoder_base->protocol->decoder->serialize) { + status = decoder_base->protocol->decoder->serialize(decoder_base, flipper_format, preset); + } + + return status; +} + +bool subghz_protocol_decoder_base_deserialize( + SubGhzProtocolDecoderBase* decoder_base, + FlipperFormat* flipper_format) { + bool status = false; + + if(decoder_base->protocol && decoder_base->protocol->decoder && + decoder_base->protocol->decoder->deserialize) { + status = decoder_base->protocol->decoder->deserialize(decoder_base, flipper_format); + } + + return status; +} + +uint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base) { + uint8_t hash = 0; + + if(decoder_base->protocol && decoder_base->protocol->decoder && + decoder_base->protocol->decoder->get_hash_data) { + hash = decoder_base->protocol->decoder->get_hash_data(decoder_base); + } + + return hash; +} diff --git a/applications/main/subghz/protocols/base.h b/applications/main/subghz/protocols/base.h new file mode 100644 index 000000000..1f3d3e1be --- /dev/null +++ b/applications/main/subghz/protocols/base.h @@ -0,0 +1,88 @@ +#pragma once + +#include "../types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzProtocolDecoderBase SubGhzProtocolDecoderBase; + +typedef void ( + *SubGhzProtocolDecoderBaseRxCallback)(SubGhzProtocolDecoderBase* instance, void* context); + +typedef void (*SubGhzProtocolDecoderBaseSerialize)( + SubGhzProtocolDecoderBase* decoder_base, + FuriString* output); + +struct SubGhzProtocolDecoderBase { + // Decoder general section + const SubGhzProtocol* protocol; + + // Callback section + SubGhzProtocolDecoderBaseRxCallback callback; + void* context; +}; + +/** + * Set a callback upon completion of successful decoding of one of the protocols. + * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance + * @param callback Callback, SubGhzProtocolDecoderBaseRxCallback + * @param context Context + */ +void subghz_protocol_decoder_base_set_decoder_callback( + SubGhzProtocolDecoderBase* decoder_base, + SubGhzProtocolDecoderBaseRxCallback callback, + void* context); + +/** + * Getting a textual representation of the received data. + * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance + * @param output Resulting text + */ +bool subghz_protocol_decoder_base_get_string( + SubGhzProtocolDecoderBase* decoder_base, + FuriString* output); + +/** + * Serialize data SubGhzProtocolDecoderBase. + * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_base_serialize( + SubGhzProtocolDecoderBase* decoder_base, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBase. + * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_base_deserialize( + SubGhzProtocolDecoderBase* decoder_base, + FlipperFormat* flipper_format); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base); + +// Encoder Base +typedef struct SubGhzProtocolEncoderBase SubGhzProtocolEncoderBase; + +struct SubGhzProtocolEncoderBase { + // Decoder general section + const SubGhzProtocol* protocol; + + // Callback section +}; + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/protocols/bett.c b/applications/main/subghz/protocols/bett.c new file mode 100644 index 000000000..644d80fd8 --- /dev/null +++ b/applications/main/subghz/protocols/bett.c @@ -0,0 +1,342 @@ +#include "bett.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +// protocol BERNER / ELKA / TEDSEN / TELETASTER +#define TAG "SubGhzProtocolBETT" + +#define DIP_P 0b11 //(+) +#define DIP_O 0b10 //(0) +#define DIP_N 0b00 //(-) + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c" +#define SHOW_DIP_P(dip, check_dip) \ + ((((dip >> 0x8) >> 0x8) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_') + +static const SubGhzBlockConst subghz_protocol_bett_const = { + .te_short = 340, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 18, +}; + +struct SubGhzProtocolDecoderBETT { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderBETT { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + BETTDecoderStepReset = 0, + BETTDecoderStepSaveDuration, + BETTDecoderStepCheckDuration, +} BETTDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_bett_decoder = { + .alloc = subghz_protocol_decoder_bett_alloc, + .free = subghz_protocol_decoder_bett_free, + + .feed = subghz_protocol_decoder_bett_feed, + .reset = subghz_protocol_decoder_bett_reset, + + .get_hash_data = subghz_protocol_decoder_bett_get_hash_data, + .serialize = subghz_protocol_decoder_bett_serialize, + .deserialize = subghz_protocol_decoder_bett_deserialize, + .get_string = subghz_protocol_decoder_bett_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_bett_encoder = { + .alloc = subghz_protocol_encoder_bett_alloc, + .free = subghz_protocol_encoder_bett_free, + + .deserialize = subghz_protocol_encoder_bett_deserialize, + .stop = subghz_protocol_encoder_bett_stop, + .yield = subghz_protocol_encoder_bett_yield, +}; + +const SubGhzProtocol subghz_protocol_bett = { + .name = SUBGHZ_PROTOCOL_BETT_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_bett_decoder, + .encoder = &subghz_protocol_bett_encoder, +}; + +void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderBETT* instance = malloc(sizeof(SubGhzProtocolEncoderBETT)); + + instance->base.protocol = &subghz_protocol_bett; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_bett_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderBETT* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderBETT instance + * @return true On success + */ +static bool subghz_protocol_encoder_bett_get_upload(SubGhzProtocolEncoderBETT* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_long); + } + } + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_bett_const.te_short + + subghz_protocol_bett_const.te_long * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_bett_const.te_long + subghz_protocol_bett_const.te_long * 7); + } + return true; +} + +bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderBETT* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_bett_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_bett_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_bett_stop(void* context) { + SubGhzProtocolEncoderBETT* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_bett_yield(void* context) { + SubGhzProtocolEncoderBETT* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_bett_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderBETT* instance = malloc(sizeof(SubGhzProtocolDecoderBETT)); + instance->base.protocol = &subghz_protocol_bett; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_bett_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + free(instance); +} + +void subghz_protocol_decoder_bett_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + instance->decoder.parser_step = BETTDecoderStepReset; +} + +void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + + switch(instance->decoder.parser_step) { + case BETTDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) < + (subghz_protocol_bett_const.te_delta * 15))) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = BETTDecoderStepCheckDuration; + } + break; + case BETTDecoderStepSaveDuration: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) < + (subghz_protocol_bett_const.te_delta * 15)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_bett_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + if((DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) < + subghz_protocol_bett_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) < + subghz_protocol_bett_const.te_delta * 3)) { + instance->decoder.parser_step = BETTDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + } + } + break; + case BETTDecoderStepCheckDuration: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) < + subghz_protocol_bett_const.te_delta * 3) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = BETTDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) < + subghz_protocol_bett_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = BETTDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_bett_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_bett_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_bett_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + uint32_t data = (uint32_t)(instance->generic.data & 0x3FFFF); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%05lX\r\n" + " +: " DIP_PATTERN "\r\n" + " o: " DIP_PATTERN "\r\n" + " -: " DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + data, + SHOW_DIP_P(data, DIP_P), + SHOW_DIP_P(data, DIP_O), + SHOW_DIP_P(data, DIP_N)); +} diff --git a/applications/main/subghz/protocols/bett.h b/applications/main/subghz/protocols/bett.h new file mode 100644 index 000000000..c0ce0b7f4 --- /dev/null +++ b/applications/main/subghz/protocols/bett.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_BETT_NAME "BETT" + +typedef struct SubGhzProtocolDecoderBETT SubGhzProtocolDecoderBETT; +typedef struct SubGhzProtocolEncoderBETT SubGhzProtocolEncoderBETT; + +extern const SubGhzProtocolDecoder subghz_protocol_bett_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_bett_encoder; +extern const SubGhzProtocol subghz_protocol_bett; + +/** + * Allocate SubGhzProtocolEncoderBETT. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderBETT* pointer to a SubGhzProtocolEncoderBETT instance + */ +void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderBETT. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + */ +void subghz_protocol_encoder_bett_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + */ +void subghz_protocol_encoder_bett_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_bett_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderBETT. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderBETT* pointer to a SubGhzProtocolDecoderBETT instance + */ +void* subghz_protocol_decoder_bett_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + */ +void subghz_protocol_decoder_bett_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + */ +void subghz_protocol_decoder_bett_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_bett_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param output Resulting text + */ +void subghz_protocol_decoder_bett_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/bin_raw.c b/applications/main/subghz/protocols/bin_raw.c new file mode 100644 index 000000000..67e0467ee --- /dev/null +++ b/applications/main/subghz/protocols/bin_raw.c @@ -0,0 +1,1120 @@ +#include "bin_raw.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" +#include +#include +#include + +#define TAG "SubGhzProtocolBinRAW" + +//change very carefully, RAM ends at the most inopportune moment +#define BIN_RAW_BUF_RAW_SIZE 2048 +#define BIN_RAW_BUF_DATA_SIZE 512 + +#define BIN_RAW_THRESHOLD_RSSI -85.0f +#define BIN_RAW_DELTA_RSSI 7.0f +#define BIN_RAW_SEARCH_CLASSES 20 +#define BIN_RAW_TE_MIN_COUNT 40 +#define BIN_RAW_BUF_MIN_DATA_COUNT 128 +#define BIN_RAW_MAX_MARKUP_COUNT 20 + +//#define BIN_RAW_DEBUG + +#ifdef BIN_RAW_DEBUG +#define bin_raw_debug(...) FURI_LOG_RAW_D(__VA_ARGS__) +#define bin_raw_debug_tag(tag, ...) \ + FURI_LOG_RAW_D("\033[0;32m[" tag "]\033[0m "); \ + FURI_LOG_RAW_D(__VA_ARGS__) +#else +#define bin_raw_debug(...) +#define bin_raw_debug_tag(...) +#endif + +static const SubGhzBlockConst subghz_protocol_bin_raw_const = { + .te_short = 30, + .te_long = 65000, + .te_delta = 0, + .min_count_bit_for_found = 0, +}; + +typedef enum { + BinRAWDecoderStepReset = 0, + BinRAWDecoderStepWrite, + BinRAWDecoderStepBufFull, + BinRAWDecoderStepNoParse, +} BinRAWDecoderStep; + +typedef enum { + BinRAWTypeUnknown = 0, + BinRAWTypeNoGap, + BinRAWTypeGap, + BinRAWTypeGapRecurring, + BinRAWTypeGapRolling, + BinRAWTypeGapUnknown, +} BinRAWType; + +struct BinRAW_Markup { + uint16_t byte_bias; + uint16_t bit_count; +}; +typedef struct BinRAW_Markup BinRAW_Markup; + +struct SubGhzProtocolDecoderBinRAW { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + int32_t* data_raw; + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + size_t data_raw_ind; + uint32_t te; + float adaptive_threshold_rssi; +}; + +struct SubGhzProtocolEncoderBinRAW { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + uint32_t te; +}; + +const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder = { + .alloc = subghz_protocol_decoder_bin_raw_alloc, + .free = subghz_protocol_decoder_bin_raw_free, + + .feed = subghz_protocol_decoder_bin_raw_feed, + .reset = subghz_protocol_decoder_bin_raw_reset, + + .get_hash_data = subghz_protocol_decoder_bin_raw_get_hash_data, + .serialize = subghz_protocol_decoder_bin_raw_serialize, + .deserialize = subghz_protocol_decoder_bin_raw_deserialize, + .get_string = subghz_protocol_decoder_bin_raw_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = { + .alloc = subghz_protocol_encoder_bin_raw_alloc, + .free = subghz_protocol_encoder_bin_raw_free, + + .deserialize = subghz_protocol_encoder_bin_raw_deserialize, + .stop = subghz_protocol_encoder_bin_raw_stop, + .yield = subghz_protocol_encoder_bin_raw_yield, +}; + +const SubGhzProtocol subghz_protocol_bin_raw = { + .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME, + .type = SubGhzProtocolTypeBinRAW, +#ifdef BIN_RAW_DEBUG + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#else + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_BinRAW | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#endif + .decoder = &subghz_protocol_bin_raw_decoder, + .encoder = &subghz_protocol_bin_raw_encoder, +}; + +static uint16_t subghz_protocol_bin_raw_get_full_byte(uint16_t bit_count) { + if(bit_count & 0x7) { + return (bit_count >> 3) + 1; + } else { + return (bit_count >> 3); + } +} + +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderBinRAW* instance = malloc(sizeof(SubGhzProtocolEncoderBinRAW)); + + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = BIN_RAW_BUF_DATA_SIZE * 5; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->data = malloc(instance->encoder.size_upload * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + free(instance->encoder.upload); + free(instance->data); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return true On success + */ +static bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinRAW* instance) { + furi_assert(instance); + + //we glue all the pieces of the package into 1 long sequence with left alignment, + //in the uploaded data we have right alignment. + + bin_raw_debug_tag(TAG, "Recovery of offset bits in sequences\r\n"); + uint16_t i = 0; + uint16_t ind = 0; + bin_raw_debug("\tind byte_bias\tbit_count\tbit_bias\r\n"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + uint8_t bit_bias = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + instance->data_markup[i].bit_count; + bin_raw_debug( + "\t%d\t%d\t%d :\t\t%d\r\n", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count, + bit_bias); + for(uint16_t y = instance->data_markup[i].byte_bias * 8; + y < instance->data_markup[i].byte_bias * 8 + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + bit_bias; + y++) { + subghz_protocol_blocks_set_bit_array( + subghz_protocol_blocks_get_bit_array(instance->data, y + bit_bias), + instance->data, + ind++, + BIN_RAW_BUF_DATA_SIZE); + } + i++; + } + bin_raw_debug("\r\n"); +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Restored Sequence left aligned\r\n"); + for(uint16_t y = 0; y < subghz_protocol_bin_raw_get_full_byte(ind); y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + bin_raw_debug("\r\n\tbin_count_result= %d\r\n\r\n", ind); + + bin_raw_debug_tag( + TAG, "Maximum levels encoded in upload %zu\r\n", instance->encoder.size_upload); +#endif + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( + instance->data, + ind, + instance->encoder.upload, + instance->encoder.size_upload, + instance->te, + SubGhzProtocolBlockAlignBitLeft); + + bin_raw_debug_tag(TAG, "The result %zu is levels\r\n", instance->encoder.size_upload); + bin_raw_debug_tag(TAG, "Remaining free memory %zu\r\n", memmgr_get_free_heap()); + return true; +} + +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + +#ifdef BIN_RAW_DEBUG + uint16_t i = 0; + bin_raw_debug_tag(TAG, "Download data to encoder\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + i++; + } + bin_raw_debug("\r\n\r\n"); +#endif + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_encoder_bin_raw_stop(void* context) { + SubGhzProtocolEncoderBinRAW* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context) { + SubGhzProtocolEncoderBinRAW* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderBinRAW* instance = malloc(sizeof(SubGhzProtocolDecoderBinRAW)); + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + instance->data_raw_ind = 0; + instance->data_raw = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + instance->data = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->adaptive_threshold_rssi = BIN_RAW_THRESHOLD_RSSI; + return instance; +} + +void subghz_protocol_decoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + free(instance->data_raw); + free(instance->data); + free(instance); +} + +void subghz_protocol_decoder_bin_raw_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; +#ifdef BIN_RAW_DEBUG + UNUSED(instance); +#else + instance->decoder.parser_step = BinRAWDecoderStepNoParse; + instance->data_raw_ind = 0; +#endif +} + +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + if(instance->decoder.parser_step == BinRAWDecoderStepWrite) { + if(instance->data_raw_ind == BIN_RAW_BUF_RAW_SIZE) { + instance->decoder.parser_step = BinRAWDecoderStepBufFull; + } else { + instance->data_raw[instance->data_raw_ind++] = (level ? duration : -duration); + } + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzProtocolDecoderBinRAW* instance + */ +static bool + subghz_protocol_bin_raw_check_remote_controller(SubGhzProtocolDecoderBinRAW* instance) { + struct { + float data; + uint16_t count; + } classes[BIN_RAW_SEARCH_CLASSES]; + + size_t ind = 0; + + memset(classes, 0x00, sizeof(classes)); + + uint16_t data_markup_ind = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + if(instance->data_raw_ind < 512) { + ind = + instance->data_raw_ind - + 100; //there is usually garbage at the end of the record, we exclude it from the classification + } else { + ind = 512; + } + + //sort the durations to find the shortest correlated interval + for(size_t i = 0; i < ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = (float)(abs(instance->data_raw[i])); + classes[k].count++; + break; + } else if( + DURATION_DIFF((float)(abs(instance->data_raw[i])), (classes[k].data)) < + (classes[k].data / 4)) { //if the test value does not differ by more than 25% + classes[k].data += ((float)(abs(instance->data_raw[i])) - classes[k].data) * + 0.05f; //running average k=0.05 + classes[k].count++; + break; + } + } + } + + // if(classes[BIN_RAW_SEARCH_CLASSES - 1].count != 0) { + // //filling the classifier, it means that they received an unclean signal + // return false; + // } + + //looking for the minimum te with an occurrence greater than BIN_RAW_TE_MIN_COUNT + instance->te = subghz_protocol_bin_raw_const.te_long * 2; + + bool te_ok = false; + uint16_t gap_ind = 0; + uint16_t gap_delta = 0; + uint32_t gap = 0; + int data_temp = 0; + BinRAWType bin_raw_type = BinRAWTypeUnknown; + + //sort by number of occurrences + bool swap = true; + while(swap) { + swap = false; + for(size_t i = 1; i < BIN_RAW_SEARCH_CLASSES; i++) { + if(classes[i].count > classes[i - 1].count) { + uint32_t data = classes[i - 1].data; + uint32_t count = classes[i - 1].count; + classes[i - 1].data = classes[i].data; + classes[i - 1].count = classes[i].count; + classes[i].data = data; + classes[i].count = count; + swap = true; + } + } + } +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Sorted durations\r\n"); + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + if((classes[0].count > BIN_RAW_TE_MIN_COUNT) && (classes[1].count == 0)) { + //adopted only the preamble + instance->te = (uint32_t)classes[0].data; + te_ok = true; + gap = 0; //gap no + } else { + //take the 2 most common durations + //check that there are enough + if((classes[0].count < BIN_RAW_TE_MIN_COUNT) || + (classes[1].count < (BIN_RAW_TE_MIN_COUNT >> 1))) + return false; + //arrange the first 2 date values in ascending order + if(classes[0].data > classes[1].data) { + uint32_t data = classes[1].data; + classes[0].data = classes[1].data; + classes[1].data = data; + } + + //determine the value to be corrected + for(uint8_t k = 1; k < 5; k++) { + float delta = (classes[1].data / (classes[0].data / k)); + bin_raw_debug_tag(TAG, "K_div= %f\r\n", (double)(delta)); + delta -= (uint32_t)delta; + + if((delta < 0.20f) || (delta > 0.80f)) { + instance->te = (uint32_t)classes[0].data / k; + bin_raw_debug_tag(TAG, "K= %d\r\n", k); + te_ok = true; //found a correlated duration + break; + } + } + if(!te_ok) { + //did not find the minimum TE satisfying the condition + return false; + } + bin_raw_debug_tag(TAG, "TE= %lu\r\n\r\n", instance->te); + + //looking for a gap + for(size_t k = 2; k < BIN_RAW_SEARCH_CLASSES; k++) { + if((classes[k].count > 2) && (classes[k].data > gap)) { + gap = (uint32_t)classes[k].data; + gap_delta = gap / 5; //calculate 20% deviation from ideal value + } + } + + if((gap / instance->te) < + 10) { //make an assumption, the longest gap should be more than 10 TE + gap = 0; //check that our signal has a gap greater than 10*TE + bin_raw_type = BinRAWTypeNoGap; + } else { + bin_raw_type = BinRAWTypeGap; + //looking for the last occurrence of gap + ind = instance->data_raw_ind - 1; + while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) { + ind--; + } + gap_ind = ind; + } + } + + //if we consider that there is a gap, then we divide the signal with respect to this gap + //processing input data from the end + if(bin_raw_type == BinRAWTypeGap) { + bin_raw_debug_tag(TAG, "Tinted sequence\r\n"); + ind = (BIN_RAW_BUF_DATA_SIZE * 8); + uint16_t bit_count = 0; + do { + gap_ind--; + data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te)); + bin_raw_debug("%d ", data_temp); + if(data_temp == 0) bit_count++; //there is noise in the package + for(size_t i = 0; i < abs(data_temp); i++) { + bit_count++; + if(ind) { + ind--; + } else { + break; + } + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } + } + //split into full bytes if gap is caught + if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + bit_count = 0; + + if(data_markup_ind == BIN_RAW_MAX_MARKUP_COUNT) break; + ind &= 0xFFFFFFF8; //jump to the pre whole byte + } + } while(gap_ind != 0); + if((data_markup_ind != BIN_RAW_MAX_MARKUP_COUNT) && (ind != 0)) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + } + + bin_raw_debug("\r\n\t count bit= %zu\r\n\r\n", (BIN_RAW_BUF_DATA_SIZE * 8) - ind); + + //reset the classifier and classify the received data + memset(classes, 0x00, sizeof(classes)); + + bin_raw_debug_tag(TAG, "Sort the found pieces by the number of bits in them\r\n"); + for(size_t i = 0; i < data_markup_ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = instance->data_markup[i].bit_count; + classes[k].count++; + break; + } else if(instance->data_markup[i].bit_count == (uint16_t)classes[k].data) { + classes[k].count++; + break; + } + } + } + +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + + //choose the value with the maximum repetition + data_temp = 0; + for(size_t i = 0; i < BIN_RAW_SEARCH_CLASSES; i++) { + if((classes[i].count > 1) && (data_temp < classes[i].count)) + data_temp = (int)classes[i].data; + } + + //if(data_markup_ind == 0) return false; + +#ifdef BIN_RAW_DEBUG + //output in reverse order + bin_raw_debug_tag(TAG, "Found sequences\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + uint16_t data_markup_ind_temp = data_markup_ind; + if(data_markup_ind) { + data_markup_ind_temp--; + for(size_t i = (ind / 8); i < BIN_RAW_BUF_DATA_SIZE; i++) { + if(instance->data_markup[data_markup_ind_temp].byte_bias == i) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + data_markup_ind_temp, + instance->data_markup[data_markup_ind_temp].byte_bias, + instance->data_markup[data_markup_ind_temp].bit_count); + if(data_markup_ind_temp != 0) data_markup_ind_temp--; + } + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); + } + //compare data in chunks with the same number of bits + bin_raw_debug_tag(TAG, "Analyze sequences of long %d bit\r\n\r\n", data_temp); +#endif + + //if(data_temp == 0) data_temp = (int)classes[0].data; + + if(data_temp != 0) { + //check that data in transmission is repeated every packet + for(uint16_t i = 0; i < data_markup_ind - 1; i++) { + if((instance->data_markup[i].bit_count == data_temp) && + (instance->data_markup[i + 1].bit_count == data_temp)) { + //if the number of bits in adjacent parcels is the same, compare the data + bin_raw_debug_tag( + TAG, + "Comparison of neighboring sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + i + 1, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[i + 1].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[i + 1].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i + 1].bit_count) - + 1]); + + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + if(memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[i + 1].byte_bias, + byte_count - 1) == 0) { + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRecurring\r\n\r\n"); + + //place in 1 element the offset to valid data + instance->data_markup[0].bit_count = instance->data_markup[i].bit_count; + instance->data_markup[0].byte_bias = instance->data_markup[i].byte_bias; + //markup end sign + instance->data_markup[1].bit_count = 0; + instance->data_markup[1].byte_bias = 0; + + bin_raw_type = BinRAWTypeGapRecurring; + i = data_markup_ind; + break; + } + } + } + } + + if(bin_raw_type == BinRAWTypeGap) { + // check that retry occurs every n packets + for(uint16_t i = 0; i < data_markup_ind - 2; i++) { + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + for(uint16_t y = i + 1; y < data_markup_ind - 1; y++) { + bin_raw_debug_tag( + TAG, + "Comparison every N sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + y, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[y].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[y].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count) - + 1]); + + if(byte_count == + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count)) { //if the length in bytes matches + + if((memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[y].byte_bias, + byte_count - 1) == 0) && + (memcmp( + instance->data + instance->data_markup[i + 1].byte_bias, + instance->data + instance->data_markup[y + 1].byte_bias, + byte_count - 1) == 0)) { + uint8_t index = 0; +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRolling\r\n\r\n"); + //output in reverse order + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + index = y - 1; + for(size_t z = instance->data_markup[y].byte_bias + byte_count; + z < instance->data_markup[i].byte_bias + byte_count; + z++) { + if(instance->data_markup[index].byte_bias == z) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + index, + instance->data_markup[index].byte_bias, + instance->data_markup[index].bit_count); + if(index != 0) index--; + } + bin_raw_debug("%02X ", instance->data[z]); + } + + bin_raw_debug("\r\n\r\n"); +#endif + //todo can be optimized + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, + 0x00, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + for(index = i; index < y; index++) { + instance->data_markup[index - i].bit_count = + markup_temp[y - index - 1].bit_count; + instance->data_markup[index - i].byte_bias = + markup_temp[y - index - 1].byte_bias; + } + + bin_raw_type = BinRAWTypeGapRolling; + i = data_markup_ind; + break; + } + } + } + } + } + //todo can be optimized + if(bin_raw_type == BinRAWTypeGap) { + if(data_temp != 0) { //there are sequences with the same number of bits + + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(data_temp); + uint16_t index = 0; + uint16_t it = BIN_RAW_MAX_MARKUP_COUNT; + do { + it--; + if(subghz_protocol_bin_raw_get_full_byte(markup_temp[it].bit_count) == + byte_count) { + instance->data_markup[index].bit_count = markup_temp[it].bit_count; + instance->data_markup[index].byte_bias = markup_temp[it].byte_bias; + index++; + bin_raw_type = BinRAWTypeGapUnknown; + } + } while(it != 0); + } + } + + if(bin_raw_type != BinRAWTypeGap) + return true; + else + return false; + + } else { + // if bin_raw_type == BinRAWTypeGap + bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n"); + ind = 0; + for(size_t i = 0; i < instance->data_raw_ind; i++) { + int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te)); + if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise + bin_raw_debug("%d ", data_temp); + + for(size_t k = 0; k < abs(data_temp); k++) { + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } + if(ind == BIN_RAW_BUF_DATA_SIZE * 8) { + i = instance->data_raw_ind; + break; + } + } + } + + if(ind != 0) { + bin_raw_type = BinRAWTypeNoGap; + //right alignment + uint8_t bit_bias = (subghz_protocol_bin_raw_get_full_byte(ind) << 3) - ind; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t count bit= %zu\tcount full byte= %d\tbias bit= %d\r\n\r\n", + ind, + subghz_protocol_bin_raw_get_full_byte(ind), + bit_bias); + + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + //checking that the received sequence contains useful data + bool data_check = false; + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + if(instance->data[i] != 0) { + data_check = true; + break; + } + } + + if(data_check) { + for(size_t i = subghz_protocol_bin_raw_get_full_byte(ind) - 1; i > 0; i--) { + instance->data[i] = (instance->data[i - 1] << (8 - bit_bias)) | + (instance->data[i] >> bit_bias); + } + instance->data[0] = (instance->data[0] >> bit_bias); + +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Data right alignment\r\n"); + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + instance->data_markup[0].bit_count = ind; + instance->data_markup[0].byte_bias = 0; + + return true; + } else { + return false; + } + } else { + return false; + } + } + return false; +} + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi) { + furi_assert(instance); + switch(instance->decoder.parser_step) { + case BinRAWDecoderStepReset: + + bin_raw_debug("%ld %ld :", (int32_t)rssi, (int32_t)instance->adaptive_threshold_rssi); + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + instance->data_raw_ind = 0; + memset(instance->data_raw, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + memset(instance->data, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + instance->decoder.parser_step = BinRAWDecoderStepWrite; + bin_raw_debug_tag(TAG, "RSSI\r\n"); + } else { + //adaptive noise level adjustment + instance->adaptive_threshold_rssi += (rssi - instance->adaptive_threshold_rssi) * 0.2f; + } + break; + + case BinRAWDecoderStepBufFull: + case BinRAWDecoderStepWrite: +#ifdef BIN_RAW_DEBUG + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + bin_raw_debug("\033[0;32m%ld \033[0m ", (int32_t)rssi); + } else { + bin_raw_debug("%ld ", (int32_t)rssi); + } +#endif + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\r\n\r\n"); + bin_raw_debug_tag(TAG, "Data for analysis, positive high, negative low, us\r\n"); + for(size_t i = 0; i < instance->data_raw_ind; i++) { + bin_raw_debug("%ld ", instance->data_raw[i]); + } + bin_raw_debug("\r\n\t count data= %zu\r\n\r\n", instance->data_raw_ind); +#endif + instance->decoder.parser_step = BinRAWDecoderStepReset; + instance->generic.data_count_bit = 0; + if(instance->data_raw_ind >= BIN_RAW_BUF_MIN_DATA_COUNT) { + if(subghz_protocol_bin_raw_check_remote_controller(instance)) { + bin_raw_debug_tag(TAG, "Sequence found\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && + (instance->data_markup[i].bit_count != 0)) { + instance->generic.data_count_bit += instance->data_markup[i].bit_count; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } +#endif + i++; + } + bin_raw_debug("\r\n"); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + } + break; + + default: + //if instance->decoder.parser_step == BinRAWDecoderStepNoParse or others, restore the initial state + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { + instance->decoder.parser_step = BinRAWDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + return subghz_protocol_blocks_add_bytes( + instance->data + instance->data_markup[0].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count)); +} + +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_header_cstr( + flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + flipper_format, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + flipper_format, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr( + flipper_format, "Protocol", instance->generic.protocol_name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + uint32_t temp = instance->generic.data_count_bit; + if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Bit"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + break; + } + + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + temp = instance->data_markup[i].bit_count; + if(!flipper_format_write_uint32(flipper_format, "Bit_RAW", &temp, 1)) { + FURI_LOG_E(TAG, "Bit_RAW"); + break; + } + if(!flipper_format_write_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[i].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count))) { + FURI_LOG_E(TAG, "Unable to add Data_RAW"); + break; + } + i++; + } + + res = true; + } while(false); + furi_string_free(temp_str); + return res; +} + +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:", + instance->generic.protocol_name, + instance->generic.data_count_bit); + + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(instance->generic.data_count_bit); + for(size_t i = 0; (byte_count < 36 ? i < byte_count : i < 36); i++) { + furi_string_cat_printf(output, "%02X", instance->data[i]); + } + + furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te); +} diff --git a/applications/main/subghz/protocols/bin_raw.h b/applications/main/subghz/protocols/bin_raw.h new file mode 100644 index 000000000..c63f86ce6 --- /dev/null +++ b/applications/main/subghz/protocols/bin_raw.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_BIN_RAW_NAME "BinRAW" + +typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW; +typedef struct SubGhzProtocolEncoderBinRAW SubGhzProtocolEncoderBinRAW; + +extern const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder; +extern const SubGhzProtocol subghz_protocol_bin_raw; + +/** + * Allocate SubGhzProtocolEncoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderBinRAW* pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderBinRAW. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderBinRAW* pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context); + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi); + +/** + * Serialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param output Resulting text + */ +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/came.c b/applications/main/subghz/protocols/came.c new file mode 100644 index 000000000..bed26d7d2 --- /dev/null +++ b/applications/main/subghz/protocols/came.c @@ -0,0 +1,347 @@ +#include "came.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://phreakerclub.com/447 + * + */ + +#define TAG "SubGhzProtocolCAME" +#define CAME_24_COUNT_BIT 24 +#define PRASTEL_COUNT_BIT 25 +#define PRASTEL_NAME "Prastel" +#define AIRFORCE_COUNT_BIT 18 +#define AIRFORCE_NAME "Airforce" + +static const SubGhzBlockConst subghz_protocol_came_const = { + .te_short = 320, + .te_long = 640, + .te_delta = 150, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderCame { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderCame { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + CameDecoderStepReset = 0, + CameDecoderStepFoundStartBit, + CameDecoderStepSaveDuration, + CameDecoderStepCheckDuration, +} CameDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_came_decoder = { + .alloc = subghz_protocol_decoder_came_alloc, + .free = subghz_protocol_decoder_came_free, + + .feed = subghz_protocol_decoder_came_feed, + .reset = subghz_protocol_decoder_came_reset, + + .get_hash_data = subghz_protocol_decoder_came_get_hash_data, + .serialize = subghz_protocol_decoder_came_serialize, + .deserialize = subghz_protocol_decoder_came_deserialize, + .get_string = subghz_protocol_decoder_came_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_came_encoder = { + .alloc = subghz_protocol_encoder_came_alloc, + .free = subghz_protocol_encoder_came_free, + + .deserialize = subghz_protocol_encoder_came_deserialize, + .stop = subghz_protocol_encoder_came_stop, + .yield = subghz_protocol_encoder_came_yield, +}; + +const SubGhzProtocol subghz_protocol_came = { + .name = SUBGHZ_PROTOCOL_CAME_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_came_decoder, + .encoder = &subghz_protocol_came_encoder, +}; + +void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderCame* instance = malloc(sizeof(SubGhzProtocolEncoderCame)); + + instance->base.protocol = &subghz_protocol_came; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_came_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderCame* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderCame instance + * @return true On success + */ +static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = level_duration_make( + false, + (((instance->generic.data_count_bit == CAME_24_COUNT_BIT) || + (instance->generic.data_count_bit == + subghz_protocol_came_const.min_count_bit_for_found)) ? + (uint32_t)subghz_protocol_came_const.te_short * 76 : + (uint32_t)subghz_protocol_came_const.te_short * 39)); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderCame* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_came_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_came_stop(void* context) { + SubGhzProtocolEncoderCame* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_came_yield(void* context) { + SubGhzProtocolEncoderCame* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_came_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderCame* instance = malloc(sizeof(SubGhzProtocolDecoderCame)); + instance->base.protocol = &subghz_protocol_came; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_came_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + free(instance); +} + +void subghz_protocol_decoder_came_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + instance->decoder.parser_step = CameDecoderStepReset; +} + +void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + switch(instance->decoder.parser_step) { + case CameDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_const.te_short * 56) < + subghz_protocol_came_const.te_delta * 47)) { + //Found header CAME + instance->decoder.parser_step = CameDecoderStepFoundStartBit; + } + break; + case CameDecoderStepFoundStartBit: + if(!level) { + break; + } else if( + DURATION_DIFF(duration, subghz_protocol_came_const.te_short) < + subghz_protocol_came_const.te_delta) { + //Found start bit CAME + instance->decoder.parser_step = CameDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = CameDecoderStepReset; + } + break; + case CameDecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_came_const.te_short * 4)) { + instance->decoder.parser_step = CameDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_came_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = CameDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = CameDecoderStepReset; + } + break; + case CameDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_came_const.te_short) < + subghz_protocol_came_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_came_const.te_long) < + subghz_protocol_came_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = CameDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_came_const.te_long) < + subghz_protocol_came_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_came_const.te_short) < + subghz_protocol_came_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = CameDecoderStepSaveDuration; + } else + instance->decoder.parser_step = CameDecoderStepReset; + } else { + instance->decoder.parser_step = CameDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_came_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_came_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_came_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Yek:0x%08lX\r\n", + (instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? + PRASTEL_NAME : + (instance->generic.data_count_bit == AIRFORCE_COUNT_BIT ? + AIRFORCE_NAME : + instance->generic.protocol_name)), + instance->generic.data_count_bit, + code_found_lo, + code_found_reverse_lo); +} diff --git a/applications/main/subghz/protocols/came.h b/applications/main/subghz/protocols/came.h new file mode 100644 index 000000000..253c93aae --- /dev/null +++ b/applications/main/subghz/protocols/came.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_CAME_NAME "CAME" + +typedef struct SubGhzProtocolDecoderCame SubGhzProtocolDecoderCame; +typedef struct SubGhzProtocolEncoderCame SubGhzProtocolEncoderCame; + +extern const SubGhzProtocolDecoder subghz_protocol_came_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_came_encoder; +extern const SubGhzProtocol subghz_protocol_came; + +/** + * Allocate SubGhzProtocolEncoderCame. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderCame* pointer to a SubGhzProtocolEncoderCame instance + */ +void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderCame. + * @param context Pointer to a SubGhzProtocolEncoderCame instance + */ +void subghz_protocol_encoder_came_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderCame instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderCame instance + */ +void subghz_protocol_encoder_came_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderCame instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_came_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderCame. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderCame* pointer to a SubGhzProtocolDecoderCame instance + */ +void* subghz_protocol_decoder_came_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderCame. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + */ +void subghz_protocol_decoder_came_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderCame. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + */ +void subghz_protocol_decoder_came_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_came_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderCame. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_came_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderCame. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + * @param output Resulting text + */ +void subghz_protocol_decoder_came_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/came_atomo.c b/applications/main/subghz/protocols/came_atomo.c new file mode 100644 index 000000000..d12e5976c --- /dev/null +++ b/applications/main/subghz/protocols/came_atomo.c @@ -0,0 +1,598 @@ +#include "came_atomo.h" +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoCameAtomo" + +static const SubGhzBlockConst subghz_protocol_came_atomo_const = { + .te_short = 600, + .te_long = 1200, + .te_delta = 250, + .min_count_bit_for_found = 62, +}; + +struct SubGhzProtocolDecoderCameAtomo { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + ManchesterState manchester_saved_state; +}; + +struct SubGhzProtocolEncoderCameAtomo { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + CameAtomoDecoderStepReset = 0, + CameAtomoDecoderStepDecoderData, +} CameAtomoDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_came_atomo_decoder = { + .alloc = subghz_protocol_decoder_came_atomo_alloc, + .free = subghz_protocol_decoder_came_atomo_free, + + .feed = subghz_protocol_decoder_came_atomo_feed, + .reset = subghz_protocol_decoder_came_atomo_reset, + + .get_hash_data = subghz_protocol_decoder_came_atomo_get_hash_data, + .serialize = subghz_protocol_decoder_came_atomo_serialize, + .deserialize = subghz_protocol_decoder_came_atomo_deserialize, + .get_string = subghz_protocol_decoder_came_atomo_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_came_atomo_encoder = { + .alloc = subghz_protocol_encoder_came_atomo_alloc, + .free = subghz_protocol_encoder_came_atomo_free, + + .deserialize = subghz_protocol_encoder_came_atomo_deserialize, + .stop = subghz_protocol_encoder_came_atomo_stop, + .yield = subghz_protocol_encoder_came_atomo_yield, +}; + +const SubGhzProtocol subghz_protocol_came_atomo = { + .name = SUBGHZ_PROTOCOL_CAME_ATOMO_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_came_atomo_decoder, + .encoder = &subghz_protocol_came_atomo_encoder, +}; + +static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* instance); + +void* subghz_protocol_encoder_came_atomo_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderCameAtomo* instance = malloc(sizeof(SubGhzProtocolEncoderCameAtomo)); + + instance->base.protocol = &subghz_protocol_came_atomo; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 900; //actual size 766+ + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_came_atomo_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderCameAtomo* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static LevelDuration + subghz_protocol_encoder_came_atomo_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_came_atomo_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_came_atomo_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_came_atomo_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_came_atomo_const.te_short; + data.level = true; + break; + + default: + FURI_LOG_E(TAG, "SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderCameAtomo instance + */ +static void + subghz_protocol_encoder_came_atomo_get_upload(SubGhzProtocolEncoderCameAtomo* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + uint8_t pack[8] = {}; + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_came_atomo_const.te_long * 15); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_long * 60); + + for(uint8_t i = 0; i < 8; i++) { + pack[0] = (instance->generic.data_2 >> 56); + pack[1] = (instance->generic.cnt >> 8); + pack[2] = (instance->generic.cnt & 0xFF); + pack[3] = ((instance->generic.data_2 >> 32) & 0xFF); + pack[4] = ((instance->generic.data_2 >> 24) & 0xFF); + pack[5] = ((instance->generic.data_2 >> 16) & 0xFF); + pack[6] = ((instance->generic.data_2 >> 8) & 0xFF); + pack[7] = (instance->generic.data_2 & 0xFF); + + if(pack[0] == 0x7F) { + pack[0] = 0; + } else { + pack[0] += (i + 1); + } + + atomo_encrypt(pack); + uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; + uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7]; + instance->generic.data = (uint64_t)hi << 32 | lo; + + instance->generic.data ^= 0xFFFFFFFFFFFFFFFF; + instance->generic.data >>= 4; + instance->generic.data &= 0xFFFFFFFFFFFFFFF; + + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_came_atomo_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_short); + + for(uint8_t i = (instance->generic.data_count_bit - 2); i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, !bit_read(instance->generic.data, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_came_atomo_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, !bit_read(instance->generic.data, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_came_atomo_add_duration_to_upload(result); + } + instance->encoder.upload[index] = + subghz_protocol_encoder_came_atomo_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + //Send pause + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_delta * 272); + } + instance->encoder.size_upload = index; + instance->generic.cnt_2++; + pack[0] = (instance->generic.cnt_2); + pack[1] = (instance->generic.cnt >> 8); + pack[2] = (instance->generic.cnt & 0xFF); + pack[3] = ((instance->generic.data_2 >> 32) & 0xFF); + pack[4] = ((instance->generic.data_2 >> 24) & 0xFF); + pack[5] = ((instance->generic.data_2 >> 16) & 0xFF); + pack[6] = ((instance->generic.data_2 >> 8) & 0xFF); + pack[7] = (instance->generic.data_2 & 0xFF); + + atomo_encrypt(pack); + uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; + uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7]; + instance->generic.data = (uint64_t)hi << 32 | lo; + + instance->generic.data ^= 0xFFFFFFFFFFFFFFFF; + instance->generic.data >>= 4; + instance->generic.data &= 0xFFFFFFFFFFFFFFF; +} + +bool subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderCameAtomo* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_came_atomo_remote_controller(&instance->generic); + subghz_protocol_encoder_came_atomo_get_upload(instance); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_came_atomo_stop(void* context) { + SubGhzProtocolEncoderCameAtomo* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_came_atomo_yield(void* context) { + SubGhzProtocolEncoderCameAtomo* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_came_atomo_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderCameAtomo* instance = malloc(sizeof(SubGhzProtocolDecoderCameAtomo)); + instance->base.protocol = &subghz_protocol_came_atomo; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_came_atomo_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + free(instance); +} + +void subghz_protocol_decoder_came_atomo_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + instance->decoder.parser_step = CameAtomoDecoderStepReset; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + + ManchesterEvent event = ManchesterEventReset; + switch(instance->decoder.parser_step) { + case CameAtomoDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 60) < + subghz_protocol_came_atomo_const.te_delta * 40)) { + //Found header CAME + instance->decoder.parser_step = CameAtomoDecoderStepDecoderData; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 1; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } + break; + case CameAtomoDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_short) < + subghz_protocol_came_atomo_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long) < + subghz_protocol_came_atomo_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= ((uint32_t)subghz_protocol_came_atomo_const.te_long * 2 + + subghz_protocol_came_atomo_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_came_atomo_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 1; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } else { + instance->decoder.parser_step = CameAtomoDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_short) < + subghz_protocol_came_atomo_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long) < + subghz_protocol_came_atomo_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = CameAtomoDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* instance) { + /* + * ***SkorP ver.*** + * 0x1fafef3ed0f7d9ef + * 0x185fcc1531ee86e7 + * 0x184fa96912c567ff + * 0x187f8a42f3dc38f7 + * 0x186f63915492a5cd + * 0x181f40bab58bfac5 + * 0x180f25c696a01bdd + * 0x183f06ed77b944d5 + * 0x182ef661d83d21a9 + * 0x18ded54a39247ea1 + * 0x18ceb0361a0f9fb9 + * 0x18fe931dfb16c0b1 + * 0x18ee7ace5c585d8b + * ........ + * transmission consists of 99 parcels with increasing counter while holding down the button + * with each new press, the counter in the encrypted part increases + * + * 0x1FAFF13ED0F7D9EF + * 0x1FAFF11ED0F7D9EF + * 0x1FAFF10ED0F7D9EF + * 0x1FAFF0FED0F7D9EF + * 0x1FAFF0EED0F7D9EF + * 0x1FAFF0DED0F7D9EF + * 0x1FAFF0CED0F7D9EF + * 0x1FAFF0BED0F7D9EF + * 0x1FAFF0AED0F7D9EF + * + * where 0x1FAF - parcel counter, 0хF0A - button press counter, + * 0xED0F7D9E - serial number, 0хF - key + * 0x1FAF parcel counter - 1 in the parcel queue ^ 0x185F = 0x07F0 + * 0x185f ^ 0x185F = 0x0000 + * 0x184f ^ 0x185F = 0x0010 + * 0x187f ^ 0x185F = 0x0020 + * ..... + * 0x182e ^ 0x185F = 0x0071 + * 0x18de ^ 0x185F = 0x0081 + * ..... + * 0x1e43 ^ 0x185F = 0x061C + * where the last nibble is incremented every 8 samples + * + * Decode + * + * 0x1cf6931dfb16c0b1 => 0x1cf6 + * 0x1cf6 ^ 0x185F = 0x04A9 + * 0x04A9 => 0x04A = 74 (dec) + * 74+1 % 32(atomo_magic_xor) = 11 + * GET atomo_magic_xor[11] = 0xXXXXXXXXXXXXXXXX + * 0x931dfb16c0b1 ^ 0xXXXXXXXXXXXXXXXX = 0xEF3ED0F7D9EF + * 0xEF3 ED0F7D9E F => 0xEF3 - CNT, 0xED0F7D9E - SN, 0xF - key + * + * ***Eng1n33r ver. (actual)*** + * 0x1FF08D9924984115 - received data + * 0x00F7266DB67BEEA0 - inverted data + * 0x0501FD0000A08300 - decrypted data, + * where: 0x05 - Button hold-cycle counter (8-bit, from 0 to 0x7F) + * 0x01FD - Parcel counter (normal 16-bit counter) + * 0x0000A083 - Serial number (32-bit) + * 0x0 - Button code (4-bit, 0x0 - #1 left-up; 0x2 - #2 right-up; 0x4 - #3 left-down; 0x6 - #4 right-down) + * 0x0 - Last zero nibble + * */ + + instance->data ^= 0xFFFFFFFFFFFFFFFF; + instance->data <<= 4; + + uint8_t pack[8] = {}; + pack[0] = (instance->data >> 56); + pack[1] = ((instance->data >> 48) & 0xFF); + pack[2] = ((instance->data >> 40) & 0xFF); + pack[3] = ((instance->data >> 32) & 0xFF); + pack[4] = ((instance->data >> 24) & 0xFF); + pack[5] = ((instance->data >> 16) & 0xFF); + pack[6] = ((instance->data >> 8) & 0xFF); + pack[7] = (instance->data & 0xFF); + + atomo_decrypt(pack); + + instance->cnt_2 = pack[0]; + instance->cnt = (uint16_t)pack[1] << 8 | pack[2]; + instance->serial = (uint32_t)(pack[3]) << 24 | pack[4] << 16 | pack[5] << 8 | pack[6]; + + uint8_t btn_decode = (pack[7] >> 4); + if(btn_decode == 0x0) { + instance->btn = 0x1; + } + if(btn_decode == 0x2) { + instance->btn = 0x2; + } + if(btn_decode == 0x4) { + instance->btn = 0x3; + } + if(btn_decode == 0x6) { + instance->btn = 0x4; + } + + uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; + uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7]; + instance->data_2 = (uint64_t)hi << 32 | lo; +} + +void atomo_encrypt(uint8_t* buff) { + uint8_t tmpB = (~buff[0] + 1) & 0x7F; + + uint8_t bitCnt = 8; + while(bitCnt < 59) { + if((tmpB & 0x18) && (((tmpB / 8) & 3) != 3)) { + tmpB = ((tmpB << 1) & 0xFF) | 1; + } else { + tmpB = (tmpB << 1) & 0xFF; + } + + if(tmpB & 0x80) { + buff[bitCnt / 8] ^= (0x80 >> (bitCnt & 7)); + } + + bitCnt++; + } + + buff[0] = (buff[0] ^ 5) & 0x7F; +} + +void atomo_decrypt(uint8_t* buff) { + buff[0] = (buff[0] ^ 5) & 0x7F; + uint8_t tmpB = (-buff[0]) & 0x7F; + + uint8_t bitCnt = 8; + while(bitCnt < 59) { + if((tmpB & 0x18) && (((tmpB / 8) & 3) != 3)) { + tmpB = ((tmpB << 1) & 0xFF) | 1; + } else { + tmpB = (tmpB << 1) & 0xFF; + } + + if(tmpB & 0x80) { + buff[bitCnt / 8] ^= (0x80 >> (bitCnt & 7)); + } + + bitCnt++; + } +} + +uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_came_atomo_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_came_atomo_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + subghz_protocol_came_atomo_remote_controller(&instance->generic); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%08lX%08lX\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Pcl_Cnt:0x%04lX\r\n" + "Btn_Cnt:0x%02X", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt, + instance->generic.cnt_2); +} diff --git a/applications/main/subghz/protocols/came_atomo.h b/applications/main/subghz/protocols/came_atomo.h new file mode 100644 index 000000000..736aee850 --- /dev/null +++ b/applications/main/subghz/protocols/came_atomo.h @@ -0,0 +1,110 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_CAME_ATOMO_NAME "CAME Atomo" + +typedef struct SubGhzProtocolDecoderCameAtomo SubGhzProtocolDecoderCameAtomo; +typedef struct SubGhzProtocolEncoderCameAtomo SubGhzProtocolEncoderCameAtomo; + +extern const SubGhzProtocolDecoder subghz_protocol_came_atomo_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_came_atomo_encoder; +extern const SubGhzProtocol subghz_protocol_came_atomo; + +void atomo_decrypt(uint8_t* buff); + +void atomo_encrypt(uint8_t* buff); + +/** + * Allocate SubGhzProtocolEncoderCameAtomo. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderCameAtomo* pointer to a SubGhzProtocolEncoderCameAtomo instance + */ +void* subghz_protocol_encoder_came_atomo_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderCameAtomo. + * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance + */ +void subghz_protocol_encoder_came_atomo_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance + */ +void subghz_protocol_encoder_came_atomo_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_came_atomo_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderCameAtomo. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderCameAtomo* pointer to a SubGhzProtocolDecoderCameAtomo instance + */ +void* subghz_protocol_decoder_came_atomo_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderCameAtomo. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + */ +void subghz_protocol_decoder_came_atomo_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderCameAtomo. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + */ +void subghz_protocol_decoder_came_atomo_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderCameAtomo. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_came_atomo_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderCameAtomo. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + * @param output Resulting text + */ +void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/came_twee.c b/applications/main/subghz/protocols/came_twee.c new file mode 100644 index 000000000..e7eb33c42 --- /dev/null +++ b/applications/main/subghz/protocols/came_twee.c @@ -0,0 +1,468 @@ +#include "came_twee.h" +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://phreakerclub.com/forum/showthread.php?t=635&highlight=came+twin + * + */ + +#define TAG "SubGhzProtocolCAME_Twee" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \ + (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \ + (dip & 0x0001 ? '1' : '0') + +/** + * Rainbow table Came Twee. + */ +static const uint32_t came_twee_magic_numbers_xor[15] = { + 0x0E0E0E00, + 0x1D1D1D11, + 0x2C2C2C22, + 0x3B3B3B33, + 0x4A4A4A44, + 0x59595955, + 0x68686866, + 0x77777777, + 0x86868688, + 0x95959599, + 0xA4A4A4AA, + 0xB3B3B3BB, + 0xC2C2C2CC, + 0xD1D1D1DD, + 0xE0E0E0EE, +}; + +static const SubGhzBlockConst subghz_protocol_came_twee_const = { + .te_short = 500, + .te_long = 1000, + .te_delta = 250, + .min_count_bit_for_found = 54, +}; + +struct SubGhzProtocolDecoderCameTwee { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + ManchesterState manchester_saved_state; +}; + +struct SubGhzProtocolEncoderCameTwee { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + CameTweeDecoderStepReset = 0, + CameTweeDecoderStepDecoderData, +} CameTweeDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_came_twee_decoder = { + .alloc = subghz_protocol_decoder_came_twee_alloc, + .free = subghz_protocol_decoder_came_twee_free, + + .feed = subghz_protocol_decoder_came_twee_feed, + .reset = subghz_protocol_decoder_came_twee_reset, + + .get_hash_data = subghz_protocol_decoder_came_twee_get_hash_data, + .serialize = subghz_protocol_decoder_came_twee_serialize, + .deserialize = subghz_protocol_decoder_came_twee_deserialize, + .get_string = subghz_protocol_decoder_came_twee_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_came_twee_encoder = { + .alloc = subghz_protocol_encoder_came_twee_alloc, + .free = subghz_protocol_encoder_came_twee_free, + + .deserialize = subghz_protocol_encoder_came_twee_deserialize, + .stop = subghz_protocol_encoder_came_twee_stop, + .yield = subghz_protocol_encoder_came_twee_yield, +}; + +const SubGhzProtocol subghz_protocol_came_twee = { + .name = SUBGHZ_PROTOCOL_CAME_TWEE_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_came_twee_decoder, + .encoder = &subghz_protocol_came_twee_encoder, +}; + +void* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderCameTwee* instance = malloc(sizeof(SubGhzProtocolEncoderCameTwee)); + + instance->base.protocol = &subghz_protocol_came_twee; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 1536; //max upload 92*14 = 1288 !!!! + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_came_twee_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderCameTwee* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static LevelDuration + subghz_protocol_encoder_came_twee_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_came_twee_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_came_twee_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_came_twee_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_came_twee_const.te_short; + data.level = true; + break; + + default: + furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderCameTwee instance + */ +static void subghz_protocol_encoder_came_twee_get_upload(SubGhzProtocolEncoderCameTwee* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + uint64_t temp_parcel = 0x003FFF7200000000; //parcel mask + + for(int i = 14; i >= 0; i--) { + temp_parcel = (temp_parcel & 0xFFFFFFFF00000000) | + (instance->generic.serial ^ came_twee_magic_numbers_xor[i]); + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(!manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_came_twee_add_duration_to_upload(result); + manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_came_twee_add_duration_to_upload(result); + } + instance->encoder.upload[index] = subghz_protocol_encoder_came_twee_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_twee_const.te_long * 51); + } + instance->encoder.size_upload = index; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_came_twee_remote_controller(SubGhzBlockGeneric* instance) { + /* Came Twee 54 bit, rolling code 15 parcels with + * a decreasing counter from 0xE to 0x0 + * with originally coded dip switches on the console 10 bit code + * + * 0x003FFF72E04A6FEE + * 0x003FFF72D17B5EDD + * 0x003FFF72C2684DCC + * 0x003FFF72B3193CBB + * 0x003FFF72A40E2BAA + * 0x003FFF72953F1A99 + * 0x003FFF72862C0988 + * 0x003FFF7277DDF877 + * 0x003FFF7268C2E766 + * 0x003FFF7259F3D655 + * 0x003FFF724AE0C544 + * 0x003FFF723B91B433 + * 0x003FFF722C86A322 + * 0x003FFF721DB79211 + * 0x003FFF720EA48100 + * + * decryption + * the last 32 bits, do XOR by the desired number, divide the result by 4, + * convert the first 16 bits of the resulting 32-bit number to bin and do + * bit-by-bit mirroring, adding up to 10 bits + * + * Example + * Step 1. 0x003FFF721DB79211 => 0x1DB79211 + * Step 4. 0x1DB79211 xor 0x1D1D1D11 => 0x00AA8F00 + * Step 4. 0x00AA8F00 / 4 => 0x002AA3C0 + * Step 5. 0x002AA3C0 => 0x002A + * Step 6. 0x002A bin => b101010 + * Step 7. b101010 => b0101010000 + * Step 8. b0101010000 => (Dip) Off ON Off ON Off ON Off Off Off Off + */ + + uint8_t cnt_parcel = (uint8_t)(instance->data & 0xF); + uint32_t data = (uint32_t)(instance->data & 0x0FFFFFFFF); + + data = (data ^ came_twee_magic_numbers_xor[cnt_parcel]); + instance->serial = data; + data /= 4; + instance->btn = (data >> 4) & 0x0F; + data >>= 16; + data = (uint16_t)subghz_protocol_blocks_reverse_key(data, 16); + instance->cnt = data >> 6; +} + +bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderCameTwee* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_came_twee_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_came_twee_remote_controller(&instance->generic); + subghz_protocol_encoder_came_twee_get_upload(instance); + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_came_twee_stop(void* context) { + SubGhzProtocolEncoderCameTwee* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_came_twee_yield(void* context) { + SubGhzProtocolEncoderCameTwee* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_came_twee_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderCameTwee* instance = malloc(sizeof(SubGhzProtocolDecoderCameTwee)); + instance->base.protocol = &subghz_protocol_came_twee; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_came_twee_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + free(instance); +} + +void subghz_protocol_decoder_came_twee_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + instance->decoder.parser_step = CameTweeDecoderStepReset; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + ManchesterEvent event = ManchesterEventReset; + switch(instance->decoder.parser_step) { + case CameTweeDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long * 51) < + subghz_protocol_came_twee_const.te_delta * 20)) { + //Found header CAME + instance->decoder.parser_step = CameTweeDecoderStepDecoderData; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongLow, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } + break; + case CameTweeDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) < + subghz_protocol_came_twee_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) < + subghz_protocol_came_twee_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= ((uint32_t)subghz_protocol_came_twee_const.te_long * 2 + + subghz_protocol_came_twee_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_came_twee_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongLow, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } else { + instance->decoder.parser_step = CameTweeDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) < + subghz_protocol_came_twee_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) < + subghz_protocol_came_twee_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = CameTweeDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_came_twee_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_came_twee_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + subghz_protocol_came_twee_remote_controller(&instance->generic); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Btn:%X\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + instance->generic.btn, + CNT_TO_DIP(instance->generic.cnt)); +} diff --git a/applications/main/subghz/protocols/came_twee.h b/applications/main/subghz/protocols/came_twee.h new file mode 100644 index 000000000..359b964da --- /dev/null +++ b/applications/main/subghz/protocols/came_twee.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_CAME_TWEE_NAME "CAME TWEE" + +typedef struct SubGhzProtocolDecoderCameTwee SubGhzProtocolDecoderCameTwee; +typedef struct SubGhzProtocolEncoderCameTwee SubGhzProtocolEncoderCameTwee; + +extern const SubGhzProtocolDecoder subghz_protocol_came_twee_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_came_twee_encoder; +extern const SubGhzProtocol subghz_protocol_came_twee; + +/** + * Allocate SubGhzProtocolEncoderCameTwee. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderCameTwee* pointer to a SubGhzProtocolEncoderCameTwee instance + */ +void* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderCameTwee. + * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance + */ +void subghz_protocol_encoder_came_twee_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance + */ +void subghz_protocol_encoder_came_twee_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_came_twee_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderCameTwee. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderCameTwee* pointer to a SubGhzProtocolDecoderCameTwee instance + */ +void* subghz_protocol_decoder_came_twee_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderCameTwee. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + */ +void subghz_protocol_decoder_came_twee_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderCameTwee. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + */ +void subghz_protocol_decoder_came_twee_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderCameTwee. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_came_twee_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderCameTwee. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + * @param output Resulting text + */ +void subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/chamberlain_code.c b/applications/main/subghz/protocols/chamberlain_code.c new file mode 100644 index 000000000..9c8e5ee4a --- /dev/null +++ b/applications/main/subghz/protocols/chamberlain_code.c @@ -0,0 +1,499 @@ +#include "chamberlain_code.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolChamb_Code" + +#define CHAMBERLAIN_CODE_BIT_STOP 0b0001 +#define CHAMBERLAIN_CODE_BIT_1 0b0011 +#define CHAMBERLAIN_CODE_BIT_0 0b0111 + +#define CHAMBERLAIN_7_CODE_MASK 0xF000000FF0F +#define CHAMBERLAIN_8_CODE_MASK 0xF00000F00F +#define CHAMBERLAIN_9_CODE_MASK 0xF000000000F + +#define CHAMBERLAIN_7_CODE_MASK_CHECK 0x10000001101 +#define CHAMBERLAIN_8_CODE_MASK_CHECK 0x1000001001 +#define CHAMBERLAIN_9_CODE_MASK_CHECK 0x10000000001 + +#define CHAMBERLAIN_7_CODE_DIP_PATTERN "%c%c%c%c%c%c%c" +#define CHAMBERLAIN_7_CODE_DATA_TO_DIP(dip) \ + (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \ + (dip & 0x0001 ? '1' : '0') + +#define CHAMBERLAIN_8_CODE_DIP_PATTERN "%c%c%c%c%cx%c%c" +#define CHAMBERLAIN_8_CODE_DATA_TO_DIP(dip) \ + (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \ + (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0') + +#define CHAMBERLAIN_9_CODE_DIP_PATTERN "%c%c%c%c%c%c%c%c%c" +#define CHAMBERLAIN_9_CODE_DATA_TO_DIP(dip) \ + (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \ + (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), \ + (dip & 0x0001 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), (dip & 0x0004 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_chamb_code_const = { + .te_short = 1000, + .te_long = 3000, + .te_delta = 200, + .min_count_bit_for_found = 10, +}; + +struct SubGhzProtocolDecoderChamb_Code { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderChamb_Code { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Chamb_CodeDecoderStepReset = 0, + Chamb_CodeDecoderStepFoundStartBit, + Chamb_CodeDecoderStepSaveDuration, + Chamb_CodeDecoderStepCheckDuration, +} Chamb_CodeDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_chamb_code_decoder = { + .alloc = subghz_protocol_decoder_chamb_code_alloc, + .free = subghz_protocol_decoder_chamb_code_free, + + .feed = subghz_protocol_decoder_chamb_code_feed, + .reset = subghz_protocol_decoder_chamb_code_reset, + + .get_hash_data = subghz_protocol_decoder_chamb_code_get_hash_data, + .serialize = subghz_protocol_decoder_chamb_code_serialize, + .deserialize = subghz_protocol_decoder_chamb_code_deserialize, + .get_string = subghz_protocol_decoder_chamb_code_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_chamb_code_encoder = { + .alloc = subghz_protocol_encoder_chamb_code_alloc, + .free = subghz_protocol_encoder_chamb_code_free, + + .deserialize = subghz_protocol_encoder_chamb_code_deserialize, + .stop = subghz_protocol_encoder_chamb_code_stop, + .yield = subghz_protocol_encoder_chamb_code_yield, +}; + +const SubGhzProtocol subghz_protocol_chamb_code = { + .name = SUBGHZ_PROTOCOL_CHAMB_CODE_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_chamb_code_decoder, + .encoder = &subghz_protocol_chamb_code_encoder, +}; + +void* subghz_protocol_encoder_chamb_code_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderChamb_Code* instance = malloc(sizeof(SubGhzProtocolEncoderChamb_Code)); + + instance->base.protocol = &subghz_protocol_chamb_code; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 24; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_chamb_code_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderChamb_Code* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static uint64_t subghz_protocol_chamb_bit_to_code(uint64_t data, uint8_t size) { + uint64_t data_res = 0; + for(uint8_t i = 0; i < size; i++) { + if(!(bit_read(data, size - i - 1))) { + data_res = data_res << 4 | CHAMBERLAIN_CODE_BIT_0; + } else { + data_res = data_res << 4 | CHAMBERLAIN_CODE_BIT_1; + } + } + return data_res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderChamb_Code instance + * @return true On success + */ +static bool + subghz_protocol_encoder_chamb_code_get_upload(SubGhzProtocolEncoderChamb_Code* instance) { + furi_assert(instance); + + uint64_t data = subghz_protocol_chamb_bit_to_code( + instance->generic.data, instance->generic.data_count_bit); + + switch(instance->generic.data_count_bit) { + case 7: + data = ((data >> 4) << 16) | (data & 0xF) << 4 | CHAMBERLAIN_7_CODE_MASK_CHECK; + break; + case 8: + data = ((data >> 12) << 16) | (data & 0xFF) << 4 | CHAMBERLAIN_8_CODE_MASK_CHECK; + break; + case 9: + data = (data << 4) | CHAMBERLAIN_9_CODE_MASK_CHECK; + break; + + default: + FURI_LOG_E(TAG, "Invalid bits count"); + return false; + break; + } +#define UPLOAD_HEX_DATA_SIZE 10 + uint8_t upload_hex_data[UPLOAD_HEX_DATA_SIZE] = {0}; + size_t upload_hex_count_bit = 0; + + //insert guard time + for(uint8_t i = 0; i < 36; i++) { + subghz_protocol_blocks_set_bit_array( + 0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); + } + + //insert data + switch(instance->generic.data_count_bit) { + case 7: + case 9: + for(uint8_t i = 44; i > 0; i--) { + if(!bit_read(data, i - 1)) { + subghz_protocol_blocks_set_bit_array( + 0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + 1, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); + } + } + break; + case 8: + for(uint8_t i = 40; i > 0; i--) { + if(!bit_read(data, i - 1)) { + subghz_protocol_blocks_set_bit_array( + 0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + 1, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); + } + } + break; + } + + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( + upload_hex_data, + upload_hex_count_bit, + instance->encoder.upload, + instance->encoder.size_upload, + subghz_protocol_chamb_code_const.te_short, + SubGhzProtocolBlockAlignBitLeft); + + return true; +} + +bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderChamb_Code* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit > + subghz_protocol_chamb_code_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_chamb_code_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_chamb_code_stop(void* context) { + SubGhzProtocolEncoderChamb_Code* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_chamb_code_yield(void* context) { + SubGhzProtocolEncoderChamb_Code* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_chamb_code_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderChamb_Code* instance = malloc(sizeof(SubGhzProtocolDecoderChamb_Code)); + instance->base.protocol = &subghz_protocol_chamb_code; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_chamb_code_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + free(instance); +} + +void subghz_protocol_decoder_chamb_code_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + instance->decoder.parser_step = Chamb_CodeDecoderStepReset; +} + +static bool subghz_protocol_chamb_code_to_bit(uint64_t* data, uint8_t size) { + uint64_t data_tmp = data[0]; + uint64_t data_res = 0; + for(uint8_t i = 0; i < size; i++) { + if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_0) { + bit_write(data_res, i, 0); + } else if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_1) { + bit_write(data_res, i, 1); + } else { + return false; + } + data_tmp >>= 4; + } + data[0] = data_res; + return true; +} + +static bool subghz_protocol_decoder_chamb_code_check_mask_and_parse( + SubGhzProtocolDecoderChamb_Code* instance) { + furi_assert(instance); + if(instance->decoder.decode_count_bit > + subghz_protocol_chamb_code_const.min_count_bit_for_found + 1) + return false; + + if((instance->decoder.decode_data & CHAMBERLAIN_7_CODE_MASK) == + CHAMBERLAIN_7_CODE_MASK_CHECK) { + instance->decoder.decode_count_bit = 7; + instance->decoder.decode_data &= ~CHAMBERLAIN_7_CODE_MASK; + instance->decoder.decode_data = (instance->decoder.decode_data >> 12) | + ((instance->decoder.decode_data >> 4) & 0xF); + } else if( + (instance->decoder.decode_data & CHAMBERLAIN_8_CODE_MASK) == + CHAMBERLAIN_8_CODE_MASK_CHECK) { + instance->decoder.decode_count_bit = 8; + instance->decoder.decode_data &= ~CHAMBERLAIN_8_CODE_MASK; + instance->decoder.decode_data = instance->decoder.decode_data >> 4 | + CHAMBERLAIN_CODE_BIT_0 << 8; //DIP 6 no use + } else if( + (instance->decoder.decode_data & CHAMBERLAIN_9_CODE_MASK) == + CHAMBERLAIN_9_CODE_MASK_CHECK) { + instance->decoder.decode_count_bit = 9; + instance->decoder.decode_data &= ~CHAMBERLAIN_9_CODE_MASK; + instance->decoder.decode_data >>= 4; + } else { + return false; + } + return subghz_protocol_chamb_code_to_bit( + &instance->decoder.decode_data, instance->decoder.decode_count_bit); +} + +void subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + switch(instance->decoder.parser_step) { + case Chamb_CodeDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 39) < + subghz_protocol_chamb_code_const.te_delta * 20)) { + //Found header Chamb_Code + instance->decoder.parser_step = Chamb_CodeDecoderStepFoundStartBit; + } + break; + case Chamb_CodeDecoderStepFoundStartBit: + if((level) && (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short) < + subghz_protocol_chamb_code_const.te_delta)) { + //Found start bit Chamb_Code + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.decode_data = instance->decoder.decode_data << 4 | + CHAMBERLAIN_CODE_BIT_STOP; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Chamb_CodeDecoderStepReset; + } + break; + case Chamb_CodeDecoderStepSaveDuration: + if(!level) { //save interval + if(duration > subghz_protocol_chamb_code_const.te_short * 5) { + if(instance->decoder.decode_count_bit >= + subghz_protocol_chamb_code_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + if(subghz_protocol_decoder_chamb_code_check_mask_and_parse(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + instance->decoder.parser_step = Chamb_CodeDecoderStepReset; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Chamb_CodeDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = Chamb_CodeDecoderStepReset; + } + break; + case Chamb_CodeDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( //Found stop bit Chamb_Code + instance->decoder.te_last, + subghz_protocol_chamb_code_const.te_short * 3) < + subghz_protocol_chamb_code_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short) < + subghz_protocol_chamb_code_const.te_delta)) { + instance->decoder.decode_data = instance->decoder.decode_data << 4 | + CHAMBERLAIN_CODE_BIT_STOP; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_chamb_code_const.te_short * 2) < + subghz_protocol_chamb_code_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 2) < + subghz_protocol_chamb_code_const.te_delta)) { + instance->decoder.decode_data = instance->decoder.decode_data << 4 | + CHAMBERLAIN_CODE_BIT_1; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_chamb_code_const.te_short) < + subghz_protocol_chamb_code_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 3) < + subghz_protocol_chamb_code_const.te_delta)) { + instance->decoder.decode_data = instance->decoder.decode_data << 4 | + CHAMBERLAIN_CODE_BIT_0; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Chamb_CodeDecoderStepReset; + } + + } else { + instance->decoder.parser_step = Chamb_CodeDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_chamb_code_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit > + subghz_protocol_chamb_code_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_chamb_code_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%03lX\r\n" + "Yek:0x%03lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_lo, + code_found_reverse_lo); + + switch(instance->generic.data_count_bit) { + case 7: + furi_string_cat_printf( + output, + "DIP:" CHAMBERLAIN_7_CODE_DIP_PATTERN "\r\n", + CHAMBERLAIN_7_CODE_DATA_TO_DIP(code_found_lo)); + break; + case 8: + furi_string_cat_printf( + output, + "DIP:" CHAMBERLAIN_8_CODE_DIP_PATTERN "\r\n", + CHAMBERLAIN_8_CODE_DATA_TO_DIP(code_found_lo)); + break; + case 9: + furi_string_cat_printf( + output, + "DIP:" CHAMBERLAIN_9_CODE_DIP_PATTERN "\r\n", + CHAMBERLAIN_9_CODE_DATA_TO_DIP(code_found_lo)); + break; + + default: + break; + } +} diff --git a/applications/main/subghz/protocols/chamberlain_code.h b/applications/main/subghz/protocols/chamberlain_code.h new file mode 100644 index 000000000..f87b64d90 --- /dev/null +++ b/applications/main/subghz/protocols/chamberlain_code.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_CHAMB_CODE_NAME "Cham_Code" + +typedef struct SubGhzProtocolDecoderChamb_Code SubGhzProtocolDecoderChamb_Code; +typedef struct SubGhzProtocolEncoderChamb_Code SubGhzProtocolEncoderChamb_Code; + +extern const SubGhzProtocolDecoder subghz_protocol_chamb_code_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_chamb_code_encoder; +extern const SubGhzProtocol subghz_protocol_chamb_code; + +/** + * Allocate SubGhzProtocolEncoderChamb_Code. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderChamb_Code* pointer to a SubGhzProtocolEncoderChamb_Code instance + */ +void* subghz_protocol_encoder_chamb_code_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderChamb_Code. + * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance + */ +void subghz_protocol_encoder_chamb_code_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance + */ +void subghz_protocol_encoder_chamb_code_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_chamb_code_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderChamb_Code. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderChamb_Code* pointer to a SubGhzProtocolDecoderChamb_Code instance + */ +void* subghz_protocol_decoder_chamb_code_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderChamb_Code. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + */ +void subghz_protocol_decoder_chamb_code_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderChamb_Code. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + */ +void subghz_protocol_decoder_chamb_code_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderChamb_Code. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_chamb_code_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderChamb_Code. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + * @param output Resulting text + */ +void subghz_protocol_decoder_chamb_code_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/clemsa.c b/applications/main/subghz/protocols/clemsa.c new file mode 100644 index 000000000..a2cb7a18b --- /dev/null +++ b/applications/main/subghz/protocols/clemsa.c @@ -0,0 +1,365 @@ +#include "clemsa.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +// protocol BERNER / ELKA / TEDSEN / TELETASTER +#define TAG "SubGhzProtocolClemsa" + +#define DIP_P 0b11 //(+) +#define DIP_O 0b10 //(0) +#define DIP_N 0b00 //(-) + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define SHOW_DIP_P(dip, check_dip) \ + ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_') + +static const SubGhzBlockConst subghz_protocol_clemsa_const = { + .te_short = 385, + .te_long = 2695, + .te_delta = 150, + .min_count_bit_for_found = 18, +}; + +struct SubGhzProtocolDecoderClemsa { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderClemsa { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + ClemsaDecoderStepReset = 0, + ClemsaDecoderStepSaveDuration, + ClemsaDecoderStepCheckDuration, +} ClemsaDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder = { + .alloc = subghz_protocol_decoder_clemsa_alloc, + .free = subghz_protocol_decoder_clemsa_free, + + .feed = subghz_protocol_decoder_clemsa_feed, + .reset = subghz_protocol_decoder_clemsa_reset, + + .get_hash_data = subghz_protocol_decoder_clemsa_get_hash_data, + .serialize = subghz_protocol_decoder_clemsa_serialize, + .deserialize = subghz_protocol_decoder_clemsa_deserialize, + .get_string = subghz_protocol_decoder_clemsa_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder = { + .alloc = subghz_protocol_encoder_clemsa_alloc, + .free = subghz_protocol_encoder_clemsa_free, + + .deserialize = subghz_protocol_encoder_clemsa_deserialize, + .stop = subghz_protocol_encoder_clemsa_stop, + .yield = subghz_protocol_encoder_clemsa_yield, +}; + +const SubGhzProtocol subghz_protocol_clemsa = { + .name = SUBGHZ_PROTOCOL_CLEMSA_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_clemsa_decoder, + .encoder = &subghz_protocol_clemsa_encoder, +}; + +void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderClemsa* instance = malloc(sizeof(SubGhzProtocolEncoderClemsa)); + + instance->base.protocol = &subghz_protocol_clemsa; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_clemsa_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderClemsa* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderClemsa instance + * @return true On success + */ +static bool subghz_protocol_encoder_clemsa_get_upload(SubGhzProtocolEncoderClemsa* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_long); + } + } + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_clemsa_const.te_short + + subghz_protocol_clemsa_const.te_long * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_clemsa_const.te_long + + subghz_protocol_clemsa_const.te_long * 7); + } + return true; +} + +bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderClemsa* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_clemsa_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_clemsa_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_clemsa_stop(void* context) { + SubGhzProtocolEncoderClemsa* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_clemsa_yield(void* context) { + SubGhzProtocolEncoderClemsa* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderClemsa* instance = malloc(sizeof(SubGhzProtocolDecoderClemsa)); + instance->base.protocol = &subghz_protocol_clemsa; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_clemsa_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + free(instance); +} + +void subghz_protocol_decoder_clemsa_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + instance->decoder.parser_step = ClemsaDecoderStepReset; +} + +void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + + switch(instance->decoder.parser_step) { + case ClemsaDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) < + subghz_protocol_clemsa_const.te_delta * 25)) { + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case ClemsaDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = ClemsaDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + break; + + case ClemsaDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) < + subghz_protocol_clemsa_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_long) < + subghz_protocol_clemsa_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) < + subghz_protocol_clemsa_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short) < + subghz_protocol_clemsa_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) < + subghz_protocol_clemsa_const.te_delta * 25) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) < + subghz_protocol_clemsa_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) < + subghz_protocol_clemsa_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + + if(instance->decoder.decode_count_bit == + subghz_protocol_clemsa_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_clemsa_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->serial = (instance->data >> 2) & 0xFFFF; + instance->btn = (instance->data & 0x03); +} + +uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_clemsa_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_clemsa_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_clemsa_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + subghz_protocol_clemsa_check_remote_controller(&instance->generic); + //uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%05lX Btn %X\r\n" + " +: " DIP_PATTERN "\r\n" + " o: " DIP_PATTERN "\r\n" + " -: " DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0x3FFFF), + instance->generic.btn, + SHOW_DIP_P(instance->generic.serial, DIP_P), + SHOW_DIP_P(instance->generic.serial, DIP_O), + SHOW_DIP_P(instance->generic.serial, DIP_N)); +} diff --git a/applications/main/subghz/protocols/clemsa.h b/applications/main/subghz/protocols/clemsa.h new file mode 100644 index 000000000..8858c1a3b --- /dev/null +++ b/applications/main/subghz/protocols/clemsa.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_CLEMSA_NAME "Clemsa" + +typedef struct SubGhzProtocolDecoderClemsa SubGhzProtocolDecoderClemsa; +typedef struct SubGhzProtocolEncoderClemsa SubGhzProtocolEncoderClemsa; + +extern const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder; +extern const SubGhzProtocol subghz_protocol_clemsa; + +/** + * Allocate SubGhzProtocolEncoderClemsa. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderClemsa* pointer to a SubGhzProtocolEncoderClemsa instance + */ +void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderClemsa. + * @param context Pointer to a SubGhzProtocolEncoderClemsa instance + */ +void subghz_protocol_encoder_clemsa_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderClemsa instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderClemsa instance + */ +void subghz_protocol_encoder_clemsa_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderClemsa instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_clemsa_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderClemsa. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderClemsa* pointer to a SubGhzProtocolDecoderClemsa instance + */ +void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderClemsa. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + */ +void subghz_protocol_decoder_clemsa_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderClemsa. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + */ +void subghz_protocol_decoder_clemsa_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderClemsa. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_clemsa_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderClemsa. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @param output Resulting text + */ +void subghz_protocol_decoder_clemsa_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/doitrand.c b/applications/main/subghz/protocols/doitrand.c new file mode 100644 index 000000000..6b31d4f27 --- /dev/null +++ b/applications/main/subghz/protocols/doitrand.c @@ -0,0 +1,356 @@ +#include "doitrand.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolDoitrand" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0001 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \ + (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x1000 ? '1' : '0'), \ + (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_doitrand_const = { + .te_short = 400, + .te_long = 1100, + .te_delta = 150, + .min_count_bit_for_found = 37, +}; + +struct SubGhzProtocolDecoderDoitrand { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderDoitrand { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + DoitrandDecoderStepReset = 0, + DoitrandDecoderStepFoundStartBit, + DoitrandDecoderStepSaveDuration, + DoitrandDecoderStepCheckDuration, +} DoitrandDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder = { + .alloc = subghz_protocol_decoder_doitrand_alloc, + .free = subghz_protocol_decoder_doitrand_free, + + .feed = subghz_protocol_decoder_doitrand_feed, + .reset = subghz_protocol_decoder_doitrand_reset, + + .get_hash_data = subghz_protocol_decoder_doitrand_get_hash_data, + .serialize = subghz_protocol_decoder_doitrand_serialize, + .deserialize = subghz_protocol_decoder_doitrand_deserialize, + .get_string = subghz_protocol_decoder_doitrand_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder = { + .alloc = subghz_protocol_encoder_doitrand_alloc, + .free = subghz_protocol_encoder_doitrand_free, + + .deserialize = subghz_protocol_encoder_doitrand_deserialize, + .stop = subghz_protocol_encoder_doitrand_stop, + .yield = subghz_protocol_encoder_doitrand_yield, +}; + +const SubGhzProtocol subghz_protocol_doitrand = { + .name = SUBGHZ_PROTOCOL_DOITRAND_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_doitrand_decoder, + .encoder = &subghz_protocol_doitrand_encoder, +}; + +void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderDoitrand* instance = malloc(sizeof(SubGhzProtocolEncoderDoitrand)); + + instance->base.protocol = &subghz_protocol_doitrand; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_doitrand_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderDoitrand* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderDoitrand instance + * @return true On success + */ +static bool subghz_protocol_encoder_doitrand_get_upload(SubGhzProtocolEncoderDoitrand* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short * 62); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short * 2 - 100); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderDoitrand* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_doitrand_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_doitrand_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_doitrand_stop(void* context) { + SubGhzProtocolEncoderDoitrand* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_doitrand_yield(void* context) { + SubGhzProtocolEncoderDoitrand* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_doitrand_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderDoitrand* instance = malloc(sizeof(SubGhzProtocolDecoderDoitrand)); + instance->base.protocol = &subghz_protocol_doitrand; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_doitrand_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + free(instance); +} + +void subghz_protocol_decoder_doitrand_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + instance->decoder.parser_step = DoitrandDecoderStepReset; +} + +void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + + switch(instance->decoder.parser_step) { + case DoitrandDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short * 62) < + subghz_protocol_doitrand_const.te_delta * 30)) { + //Found Preambula + instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit; + } + break; + case DoitrandDecoderStepFoundStartBit: + if(level && ((DURATION_DIFF(duration, (subghz_protocol_doitrand_const.te_short * 2)) < + subghz_protocol_doitrand_const.te_delta * 3))) { + //Found start bit + instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = DoitrandDecoderStepReset; + } + break; + case DoitrandDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_doitrand_const.te_short * 10 + + subghz_protocol_doitrand_const.te_delta)) { + instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_doitrand_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = DoitrandDecoderStepCheckDuration; + } + } + break; + case DoitrandDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_short) < + subghz_protocol_doitrand_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_long) < + subghz_protocol_doitrand_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_long) < + subghz_protocol_doitrand_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short) < + subghz_protocol_doitrand_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = DoitrandDecoderStepReset; + } + } else { + instance->decoder.parser_step = DoitrandDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_doitrand_check_remote_controller(SubGhzBlockGeneric* instance) { + /* +* 67892345 0 k 1 +* 0000082F5F => 00000000000000000 10 000010111101011111 +* 0002082F5F => 00000000000100000 10 000010111101011111 +* 0200082F5F => 00010000000000000 10 000010111101011111 +* 0400082F5F => 00100000000000000 10 000010111101011111 +* 0800082F5F => 01000000000000000 10 000010111101011111 +* 1000082F5F => 10000000000000000 10 000010111101011111 +* 0020082F5F => 00000001000000000 10 000010111101011111 +* 0040082F5F => 00000010000000000 10 000010111101011111 +* 0080082F5F => 00000100000000000 10 000010111101011111 +* 0100082F5F => 00001000000000000 10 000010111101011111 +* 000008AF5F => 00000000000000000 10 001010111101011111 +* 1FE208AF5F => 11111111000100000 10 001010111101011111 +* +* 0...9 - DIP +* k- KEY +*/ + instance->cnt = (instance->data >> 24) | ((instance->data >> 15) & 0x1); + instance->btn = ((instance->data >> 18) & 0x3); +} + +uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_doitrand_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_doitrand_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_doitrand_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + subghz_protocol_doitrand_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%02lX%08lX\r\n" + "Btn:%X\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.btn, + CNT_TO_DIP(instance->generic.cnt)); +} diff --git a/applications/main/subghz/protocols/doitrand.h b/applications/main/subghz/protocols/doitrand.h new file mode 100644 index 000000000..30f1fffd0 --- /dev/null +++ b/applications/main/subghz/protocols/doitrand.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_DOITRAND_NAME "Doitrand" + +typedef struct SubGhzProtocolDecoderDoitrand SubGhzProtocolDecoderDoitrand; +typedef struct SubGhzProtocolEncoderDoitrand SubGhzProtocolEncoderDoitrand; + +extern const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder; +extern const SubGhzProtocol subghz_protocol_doitrand; + +/** + * Allocate SubGhzProtocolEncoderDoitrand. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderDoitrand* pointer to a SubGhzProtocolEncoderDoitrand instance + */ +void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderDoitrand. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + */ +void subghz_protocol_encoder_doitrand_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + */ +void subghz_protocol_encoder_doitrand_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_doitrand_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderDoitrand. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderDoitrand* pointer to a SubGhzProtocolDecoderDoitrand instance + */ +void* subghz_protocol_decoder_doitrand_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + */ +void subghz_protocol_decoder_doitrand_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + */ +void subghz_protocol_decoder_doitrand_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_doitrand_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param output Resulting text + */ +void subghz_protocol_decoder_doitrand_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/dooya.c b/applications/main/subghz/protocols/dooya.c new file mode 100644 index 000000000..c70b6d54e --- /dev/null +++ b/applications/main/subghz/protocols/dooya.c @@ -0,0 +1,447 @@ +#include "dooya.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolDooya" + +#define DOYA_SINGLE_CHANNEL 0xFF + +static const SubGhzBlockConst subghz_protocol_dooya_const = { + .te_short = 366, + .te_long = 733, + .te_delta = 120, + .min_count_bit_for_found = 40, +}; + +struct SubGhzProtocolDecoderDooya { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderDooya { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + DooyaDecoderStepReset = 0, + DooyaDecoderStepFoundStartBit, + DooyaDecoderStepSaveDuration, + DooyaDecoderStepCheckDuration, +} DooyaDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_dooya_decoder = { + .alloc = subghz_protocol_decoder_dooya_alloc, + .free = subghz_protocol_decoder_dooya_free, + + .feed = subghz_protocol_decoder_dooya_feed, + .reset = subghz_protocol_decoder_dooya_reset, + + .get_hash_data = subghz_protocol_decoder_dooya_get_hash_data, + .serialize = subghz_protocol_decoder_dooya_serialize, + .deserialize = subghz_protocol_decoder_dooya_deserialize, + .get_string = subghz_protocol_decoder_dooya_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_dooya_encoder = { + .alloc = subghz_protocol_encoder_dooya_alloc, + .free = subghz_protocol_encoder_dooya_free, + + .deserialize = subghz_protocol_encoder_dooya_deserialize, + .stop = subghz_protocol_encoder_dooya_stop, + .yield = subghz_protocol_encoder_dooya_yield, +}; + +const SubGhzProtocol subghz_protocol_dooya = { + .name = SUBGHZ_PROTOCOL_DOOYA_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_dooya_decoder, + .encoder = &subghz_protocol_dooya_encoder, +}; + +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderDooya* instance = malloc(sizeof(SubGhzProtocolEncoderDooya)); + + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderDooya* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderDooya instance + * @return true On success + */ +static bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + if(bit_read(instance->generic.data, 0)) { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_long); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_short); + } + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short * 13); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long * 2); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderDooya* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_dooya_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_dooya_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_dooya_stop(void* context) { + SubGhzProtocolEncoderDooya* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_dooya_yield(void* context) { + SubGhzProtocolEncoderDooya* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderDooya* instance = malloc(sizeof(SubGhzProtocolDecoderDooya)); + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + free(instance); +} + +void subghz_protocol_decoder_dooya_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + instance->decoder.parser_step = DooyaDecoderStepReset; +} + +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + switch(instance->decoder.parser_step) { + case DooyaDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 12) < + subghz_protocol_dooya_const.te_delta * 20)) { + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + } + break; + + case DooyaDecoderStepFoundStartBit: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 2) < + subghz_protocol_dooya_const.te_delta * 3) { + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else if( + DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short * 13) < + subghz_protocol_dooya_const.te_delta * 5) { + break; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = DooyaDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_dooya_const.te_long * 4)) { + //add last bit + if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + break; + } + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_dooya_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * serial s/m ch key + * long press down X * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * + * short press down 3 * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * 3 * E1DC03053C, 40b 111000011101110000000011 0000 0101 0011 1100 + * + * press stop X * E1DC030555, 40b 111000011101110000000011 0000 0101 0101 0101 + * + * long press up X * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * + * short press up 3 * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * 3 * E1DC03051E, 40b 111000011101110000000011 0000 0101 0001 1110 + * + * serial: 3 byte serial number + * s/m: single (b0000) / multi (b0001) channel console + * ch: channel if single (always b0101) or multi + * key: 0b00010001 - long press up + * 0b00011110 - short press up + * 0b00110011 - long press down + * 0b00111100 - short press down + * 0b01010101 - press stop + * 0b01111001 - press up + down + * 0b10000000 - press up + stop + * 0b10000001 - press down + stop + * 0b11001100 - press P2 + * +*/ + + instance->serial = (instance->data >> 16); + if((instance->data >> 12) & 0x0F) { + instance->cnt = (instance->data >> 8) & 0x0F; + } else { + instance->cnt = DOYA_SINGLE_CHANNEL; + } + instance->btn = instance->data & 0xFF; +} + +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_dooya_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +/** + * Get button name. + * @param btn Button number, 8 bit + */ +static const char* subghz_protocol_dooya_get_name_button(uint8_t btn) { + const char* btn_name; + switch(btn) { + case 0b00010001: + btn_name = "Up_Long"; + break; + case 0b00011110: + btn_name = "Up_Short"; + break; + case 0b00110011: + btn_name = "Down_Long"; + break; + case 0b00111100: + btn_name = "Down_Short"; + break; + case 0b01010101: + btn_name = "Stop"; + break; + case 0b01111001: + btn_name = "Up+Down"; + break; + case 0b10000000: + btn_name = "Up+Stop"; + break; + case 0b10000001: + btn_name = "Down+Stop"; + break; + case 0b11001100: + btn_name = "P2"; + break; + default: + btn_name = "Unknown"; + break; + } + return btn_name; +} + +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%010llX\r\n" + "Sn:0x%08lX\r\n" + "Btn:%s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + subghz_protocol_dooya_get_name_button(instance->generic.btn)); + if(instance->generic.cnt == DOYA_SINGLE_CHANNEL) { + furi_string_cat_printf(output, "Ch:Single\r\n"); + } else { + furi_string_cat_printf(output, "Ch:%lu\r\n", instance->generic.cnt); + } +} diff --git a/applications/main/subghz/protocols/dooya.h b/applications/main/subghz/protocols/dooya.h new file mode 100644 index 000000000..f0cf843c0 --- /dev/null +++ b/applications/main/subghz/protocols/dooya.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_DOOYA_NAME "Dooya" + +typedef struct SubGhzProtocolDecoderDooya SubGhzProtocolDecoderDooya; +typedef struct SubGhzProtocolEncoderDooya SubGhzProtocolEncoderDooya; + +extern const SubGhzProtocolDecoder subghz_protocol_dooya_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_dooya_encoder; +extern const SubGhzProtocol subghz_protocol_dooya; + +/** + * Allocate SubGhzProtocolEncoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderDooya* pointer to a SubGhzProtocolEncoderDooya instance + */ +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderDooya. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_dooya_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderDooya* pointer to a SubGhzProtocolDecoderDooya instance + */ +void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param output Resulting text + */ +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/faac_slh.c b/applications/main/subghz/protocols/faac_slh.c new file mode 100644 index 000000000..7572bd8ab --- /dev/null +++ b/applications/main/subghz/protocols/faac_slh.c @@ -0,0 +1,512 @@ +#include "faac_slh.h" +#include "../subghz_keystore.h" +#include +#include "keeloq_common.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolFaacSLH" + +static const SubGhzBlockConst subghz_protocol_faac_slh_const = { + .te_short = 255, + .te_long = 595, + .te_delta = 100, + .min_count_bit_for_found = 64, +}; + +struct SubGhzProtocolDecoderFaacSLH { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + SubGhzKeystore* keystore; + const char* manufacture_name; +}; + +struct SubGhzProtocolEncoderFaacSLH { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + SubGhzKeystore* keystore; + const char* manufacture_name; +}; + +typedef enum { + FaacSLHDecoderStepReset = 0, + FaacSLHDecoderStepFoundPreambula, + FaacSLHDecoderStepSaveDuration, + FaacSLHDecoderStepCheckDuration, +} FaacSLHDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_faac_slh_decoder = { + .alloc = subghz_protocol_decoder_faac_slh_alloc, + .free = subghz_protocol_decoder_faac_slh_free, + + .feed = subghz_protocol_decoder_faac_slh_feed, + .reset = subghz_protocol_decoder_faac_slh_reset, + + .get_hash_data = subghz_protocol_decoder_faac_slh_get_hash_data, + .serialize = subghz_protocol_decoder_faac_slh_serialize, + .deserialize = subghz_protocol_decoder_faac_slh_deserialize, + .get_string = subghz_protocol_decoder_faac_slh_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_faac_slh_encoder = { + .alloc = subghz_protocol_encoder_faac_slh_alloc, + .free = subghz_protocol_encoder_faac_slh_free, + + .deserialize = subghz_protocol_encoder_faac_slh_deserialize, + .stop = subghz_protocol_encoder_faac_slh_stop, + .yield = subghz_protocol_encoder_faac_slh_yield, +}; + +const SubGhzProtocol subghz_protocol_faac_slh = { + .name = SUBGHZ_PROTOCOL_FAAC_SLH_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_faac_slh_decoder, + .encoder = &subghz_protocol_faac_slh_encoder, +}; + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + */ +static void subghz_protocol_faac_slh_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name); + +void* subghz_protocol_encoder_faac_slh_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderFaacSLH* instance = malloc(sizeof(SubGhzProtocolEncoderFaacSLH)); + + instance->base.protocol = &subghz_protocol_faac_slh; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_faac_slh_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderFaacSLH* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* instance) { + instance->generic.cnt++; + uint32_t fix = instance->generic.serial << 4 | instance->generic.btn; + uint32_t hop = 0; + uint32_t decrypt = 0; + uint64_t man = 0; + int res = 0; + char fixx[8] = {}; + int shiftby = 32; + for(int i = 0; i < 8; i++) { + fixx[i] = (fix >> (shiftby -= 4)) & 0xF; + } + if((instance->generic.cnt % 2) == 0) { + decrypt = fixx[6] << 28 | fixx[7] << 24 | fixx[5] << 20 | + (instance->generic.cnt & 0xFFFFF); + } else { + decrypt = fixx[2] << 28 | fixx[3] << 24 | fixx[4] << 20 | + (instance->generic.cnt & 0xFFFFF); + } + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_FAAC: + //FAAC Learning + man = subghz_protocol_keeloq_common_faac_learning( + instance->generic.seed, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + } + break; + } + } + if(hop) { + instance->generic.data = (uint64_t)fix << 32 | hop; + } + return true; +} + +bool subghz_protocol_faac_slh_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint32_t cnt, + uint32_t seed, + const char* manufacture_name, + SubGhzRadioPreset* preset) { + furi_assert(context); + // roguemaster don't steal!!! + SubGhzProtocolEncoderFaacSLH* instance = context; + instance->generic.serial = serial; + instance->generic.btn = btn; + instance->generic.cnt = (cnt & 0xFFFFF); + instance->generic.seed = seed; + instance->manufacture_name = manufacture_name; + instance->generic.data_count_bit = 64; + bool res = subghz_protocol_faac_slh_gen_data(instance); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderFaacSLH instance + * @return true On success + */ +static bool subghz_protocol_encoder_faac_slh_get_upload(SubGhzProtocolEncoderFaacSLH* instance) { + furi_assert(instance); + + subghz_protocol_faac_slh_gen_data(instance); + size_t index = 0; + size_t size_upload = 2 + (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_faac_slh_const.te_long * 2); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_long * 2); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_faac_slh_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_faac_slh_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderFaacSLH* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_E(TAG, "Missing Seed"); + break; + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + + subghz_protocol_faac_slh_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_faac_slh_get_upload(instance); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_faac_slh_stop(void* context) { + SubGhzProtocolEncoderFaacSLH* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_faac_slh_yield(void* context) { + SubGhzProtocolEncoderFaacSLH* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_faac_slh_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderFaacSLH* instance = malloc(sizeof(SubGhzProtocolDecoderFaacSLH)); + instance->base.protocol = &subghz_protocol_faac_slh; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + return instance; +} + +void subghz_protocol_decoder_faac_slh_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + free(instance); +} + +void subghz_protocol_decoder_faac_slh_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + instance->decoder.parser_step = FaacSLHDecoderStepReset; +} + +void subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + + switch(instance->decoder.parser_step) { + case FaacSLHDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long * 2) < + subghz_protocol_faac_slh_const.te_delta * 3)) { + instance->decoder.parser_step = FaacSLHDecoderStepFoundPreambula; + } + break; + case FaacSLHDecoderStepFoundPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long * 2) < + subghz_protocol_faac_slh_const.te_delta * 3)) { + //Found Preambula + instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = FaacSLHDecoderStepReset; + } + break; + case FaacSLHDecoderStepSaveDuration: + if(level) { + if(duration >= ((uint32_t)subghz_protocol_faac_slh_const.te_short * 3 + + subghz_protocol_faac_slh_const.te_delta)) { + instance->decoder.parser_step = FaacSLHDecoderStepFoundPreambula; + if(instance->decoder.decode_count_bit == + subghz_protocol_faac_slh_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = FaacSLHDecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = FaacSLHDecoderStepReset; + } + break; + case FaacSLHDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_faac_slh_const.te_short) < + subghz_protocol_faac_slh_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long) < + subghz_protocol_faac_slh_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_faac_slh_const.te_long) < + subghz_protocol_faac_slh_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_short) < + subghz_protocol_faac_slh_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = FaacSLHDecoderStepReset; + } + } else { + instance->decoder.parser_step = FaacSLHDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manifacture_name Manufacturer name + */ +static void subghz_protocol_faac_slh_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name) { + uint32_t code_fix = instance->data >> 32; + uint32_t code_hop = instance->data & 0xFFFFFFFF; + instance->serial = code_fix >> 4; + instance->btn = code_fix & 0xF; + uint32_t decrypt = 0; + uint64_t man; + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_FAAC: + // FAAC Learning + man = subghz_protocol_keeloq_common_faac_learning( + instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(code_hop, man); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + break; + } + } + instance->cnt = decrypt & 0xFFFFF; +} + +uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_faac_slh_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(res && !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_E(TAG, "Unable to add Seed"); + res = false; + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + + subghz_protocol_faac_slh_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + return res; +} + +bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_faac_slh_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_E(TAG, "Missing Seed"); + break; + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + subghz_protocol_faac_slh_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + uint32_t code_fix = instance->generic.data >> 32; + uint32_t code_hop = instance->generic.data & 0xFFFFFFFF; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%lX%08lX\r\n" + "Fix:%08lX Cnt:%05lX\r\n" + "Hop:%08lX Btn:%X\r\n" + "Sn:%07lX Sd:%08lX", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + code_fix, + instance->generic.cnt, + code_hop, + instance->generic.btn, + instance->generic.serial, + instance->generic.seed); +} diff --git a/applications/main/subghz/protocols/faac_slh.h b/applications/main/subghz/protocols/faac_slh.h new file mode 100644 index 000000000..9390da43a --- /dev/null +++ b/applications/main/subghz/protocols/faac_slh.h @@ -0,0 +1,129 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_FAAC_SLH_NAME "Faac SLH" + +typedef struct SubGhzProtocolDecoderFaacSLH SubGhzProtocolDecoderFaacSLH; +typedef struct SubGhzProtocolEncoderFaacSLH SubGhzProtocolEncoderFaacSLH; + +extern const SubGhzProtocolDecoder subghz_protocol_faac_slh_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_faac_slh_encoder; +extern const SubGhzProtocol subghz_protocol_faac_slh; + +/** + * Allocate SubGhzProtocolEncoderFaacSLH. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderFaacSLH* pointer to a SubGhzProtocolEncoderFaacSLH instance + */ +void* subghz_protocol_encoder_faac_slh_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderFaacSLH. + * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance + */ +void subghz_protocol_encoder_faac_slh_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 28 bit + * @param btn Button number, 4 bit + * @param cnt Counter value, 16 bit + * @param seed Seed value, 32 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_faac_slh_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint32_t cnt, + uint32_t seed, + const char* manufacture_name, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance + */ +void subghz_protocol_encoder_faac_slh_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_faac_slh_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderFaacSLH. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderFaacSLH* pointer to a SubGhzProtocolDecoderFaacSLH instance + */ +void* subghz_protocol_decoder_faac_slh_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderFaacSLH. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + */ +void subghz_protocol_decoder_faac_slh_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderFaacSLH. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + */ +void subghz_protocol_decoder_faac_slh_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderFaacSLH. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_faac_slh_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderFaacSLH. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + * @param output Resulting text + */ +void subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/gate_tx.c b/applications/main/subghz/protocols/gate_tx.c new file mode 100644 index 000000000..4c7c2d484 --- /dev/null +++ b/applications/main/subghz/protocols/gate_tx.c @@ -0,0 +1,334 @@ +#include "gate_tx.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolGateTx" + +static const SubGhzBlockConst subghz_protocol_gate_tx_const = { + .te_short = 350, + .te_long = 700, + .te_delta = 100, + .min_count_bit_for_found = 24, +}; + +struct SubGhzProtocolDecoderGateTx { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderGateTx { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + GateTXDecoderStepReset = 0, + GateTXDecoderStepFoundStartBit, + GateTXDecoderStepSaveDuration, + GateTXDecoderStepCheckDuration, +} GateTXDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_gate_tx_decoder = { + .alloc = subghz_protocol_decoder_gate_tx_alloc, + .free = subghz_protocol_decoder_gate_tx_free, + + .feed = subghz_protocol_decoder_gate_tx_feed, + .reset = subghz_protocol_decoder_gate_tx_reset, + + .get_hash_data = subghz_protocol_decoder_gate_tx_get_hash_data, + .serialize = subghz_protocol_decoder_gate_tx_serialize, + .deserialize = subghz_protocol_decoder_gate_tx_deserialize, + .get_string = subghz_protocol_decoder_gate_tx_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_gate_tx_encoder = { + .alloc = subghz_protocol_encoder_gate_tx_alloc, + .free = subghz_protocol_encoder_gate_tx_free, + + .deserialize = subghz_protocol_encoder_gate_tx_deserialize, + .stop = subghz_protocol_encoder_gate_tx_stop, + .yield = subghz_protocol_encoder_gate_tx_yield, +}; + +const SubGhzProtocol subghz_protocol_gate_tx = { + .name = SUBGHZ_PROTOCOL_GATE_TX_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_gate_tx_decoder, + .encoder = &subghz_protocol_gate_tx_encoder, +}; + +void* subghz_protocol_encoder_gate_tx_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderGateTx* instance = malloc(sizeof(SubGhzProtocolEncoderGateTx)); + + instance->base.protocol = &subghz_protocol_gate_tx; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop) + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_gate_tx_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderGateTx* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderGateTx instance + * @return true On success + */ +static bool subghz_protocol_encoder_gate_tx_get_upload(SubGhzProtocolEncoderGateTx* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_short * 49); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_long); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderGateTx* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_gate_tx_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_gate_tx_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_gate_tx_stop(void* context) { + SubGhzProtocolEncoderGateTx* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_gate_tx_yield(void* context) { + SubGhzProtocolEncoderGateTx* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_gate_tx_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderGateTx* instance = malloc(sizeof(SubGhzProtocolDecoderGateTx)); + instance->base.protocol = &subghz_protocol_gate_tx; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_gate_tx_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + free(instance); +} + +void subghz_protocol_decoder_gate_tx_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + instance->decoder.parser_step = GateTXDecoderStepReset; +} + +void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + + switch(instance->decoder.parser_step) { + case GateTXDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_short * 47) < + subghz_protocol_gate_tx_const.te_delta * 47)) { + //Found Preambula + instance->decoder.parser_step = GateTXDecoderStepFoundStartBit; + } + break; + case GateTXDecoderStepFoundStartBit: + if(level && ((DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_long) < + subghz_protocol_gate_tx_const.te_delta * 3))) { + //Found start bit + instance->decoder.parser_step = GateTXDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = GateTXDecoderStepReset; + } + break; + case GateTXDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_gate_tx_const.te_short * 10 + + subghz_protocol_gate_tx_const.te_delta)) { + instance->decoder.parser_step = GateTXDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_gate_tx_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = GateTXDecoderStepCheckDuration; + } + } + break; + case GateTXDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gate_tx_const.te_short) < + subghz_protocol_gate_tx_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_long) < + subghz_protocol_gate_tx_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = GateTXDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gate_tx_const.te_long) < + subghz_protocol_gate_tx_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_short) < + subghz_protocol_gate_tx_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = GateTXDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = GateTXDecoderStepReset; + } + } else { + instance->decoder.parser_step = GateTXDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_gate_tx_check_remote_controller(SubGhzBlockGeneric* instance) { + uint32_t code_found_reverse = + subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); + + instance->serial = (code_found_reverse & 0xFF) << 12 | + ((code_found_reverse >> 8) & 0xFF) << 4 | + ((code_found_reverse >> 20) & 0x0F); + instance->btn = ((code_found_reverse >> 16) & 0x0F); +} + +uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_gate_tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_gate_tx_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_gate_tx_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + subghz_protocol_gate_tx_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%06lX\r\n" + "Sn:%05lX Btn:%X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFF), + instance->generic.serial, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/gate_tx.h b/applications/main/subghz/protocols/gate_tx.h new file mode 100644 index 000000000..4bfba3597 --- /dev/null +++ b/applications/main/subghz/protocols/gate_tx.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_GATE_TX_NAME "GateTX" + +typedef struct SubGhzProtocolDecoderGateTx SubGhzProtocolDecoderGateTx; +typedef struct SubGhzProtocolEncoderGateTx SubGhzProtocolEncoderGateTx; + +extern const SubGhzProtocolDecoder subghz_protocol_gate_tx_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_gate_tx_encoder; +extern const SubGhzProtocol subghz_protocol_gate_tx; + +/** + * Allocate SubGhzProtocolEncoderGateTx. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderGateTx* pointer to a SubGhzProtocolEncoderGateTx instance + */ +void* subghz_protocol_encoder_gate_tx_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderGateTx. + * @param context Pointer to a SubGhzProtocolEncoderGateTx instance + */ +void subghz_protocol_encoder_gate_tx_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderGateTx instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderGateTx instance + */ +void subghz_protocol_encoder_gate_tx_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderGateTx instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_gate_tx_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderGateTx. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderGateTx* pointer to a SubGhzProtocolDecoderGateTx instance + */ +void* subghz_protocol_decoder_gate_tx_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderGateTx. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + */ +void subghz_protocol_decoder_gate_tx_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderGateTx. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + */ +void subghz_protocol_decoder_gate_tx_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderGateTx. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_gate_tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderGateTx. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + * @param output Resulting text + */ +void subghz_protocol_decoder_gate_tx_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/holtek.c b/applications/main/subghz/protocols/holtek.c new file mode 100644 index 000000000..8aaad3b71 --- /dev/null +++ b/applications/main/subghz/protocols/holtek.c @@ -0,0 +1,374 @@ +#include "holtek.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://pdf1.alldatasheet.com/datasheet-pdf/view/82103/HOLTEK/HT640.html + * https://fccid.io/OJM-CMD-HHLR-XXXA + * + */ + +#define TAG "SubGhzProtocolHoltek" + +#define HOLTEK_HEADER_MASK 0xF000000000 +#define HOLTEK_HEADER 0x5000000000 + +static const SubGhzBlockConst subghz_protocol_holtek_const = { + .te_short = 430, + .te_long = 870, + .te_delta = 100, + .min_count_bit_for_found = 40, +}; + +struct SubGhzProtocolDecoderHoltek { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderHoltek { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + HoltekDecoderStepReset = 0, + HoltekDecoderStepFoundStartBit, + HoltekDecoderStepSaveDuration, + HoltekDecoderStepCheckDuration, +} HoltekDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_holtek_decoder = { + .alloc = subghz_protocol_decoder_holtek_alloc, + .free = subghz_protocol_decoder_holtek_free, + + .feed = subghz_protocol_decoder_holtek_feed, + .reset = subghz_protocol_decoder_holtek_reset, + + .get_hash_data = subghz_protocol_decoder_holtek_get_hash_data, + .serialize = subghz_protocol_decoder_holtek_serialize, + .deserialize = subghz_protocol_decoder_holtek_deserialize, + .get_string = subghz_protocol_decoder_holtek_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_holtek_encoder = { + .alloc = subghz_protocol_encoder_holtek_alloc, + .free = subghz_protocol_encoder_holtek_free, + + .deserialize = subghz_protocol_encoder_holtek_deserialize, + .stop = subghz_protocol_encoder_holtek_stop, + .yield = subghz_protocol_encoder_holtek_yield, +}; + +const SubGhzProtocol subghz_protocol_holtek = { + .name = SUBGHZ_PROTOCOL_HOLTEK_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_holtek_decoder, + .encoder = &subghz_protocol_holtek_encoder, +}; + +void* subghz_protocol_encoder_holtek_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHoltek* instance = malloc(sizeof(SubGhzProtocolEncoderHoltek)); + + instance->base.protocol = &subghz_protocol_holtek; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_holtek_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHoltek* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHoltek instance + * @return true On success + */ +static bool subghz_protocol_encoder_holtek_get_upload(SubGhzProtocolEncoderHoltek* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_short * 36); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHoltek* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_holtek_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_holtek_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_holtek_stop(void* context) { + SubGhzProtocolEncoderHoltek* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_holtek_yield(void* context) { + SubGhzProtocolEncoderHoltek* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_holtek_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHoltek* instance = malloc(sizeof(SubGhzProtocolDecoderHoltek)); + instance->base.protocol = &subghz_protocol_holtek; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_holtek_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + free(instance); +} + +void subghz_protocol_decoder_holtek_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + instance->decoder.parser_step = HoltekDecoderStepReset; +} + +void subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + + switch(instance->decoder.parser_step) { + case HoltekDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short * 36) < + subghz_protocol_holtek_const.te_delta * 36)) { + //Found Preambula + instance->decoder.parser_step = HoltekDecoderStepFoundStartBit; + } + break; + case HoltekDecoderStepFoundStartBit: + if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short) < + subghz_protocol_holtek_const.te_delta)) { + //Found StartBit + instance->decoder.parser_step = HoltekDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = HoltekDecoderStepReset; + } + break; + case HoltekDecoderStepSaveDuration: + //save duration + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_holtek_const.te_short * 10 + + subghz_protocol_holtek_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_holtek_const.min_count_bit_for_found) { + if((instance->decoder.decode_data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = HoltekDecoderStepFoundStartBit; + break; + } else { + instance->decoder.te_last = duration; + + instance->decoder.parser_step = HoltekDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = HoltekDecoderStepReset; + } + break; + case HoltekDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_holtek_const.te_short) < + subghz_protocol_holtek_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_long) < + subghz_protocol_holtek_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = HoltekDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_holtek_const.te_long) < + subghz_protocol_holtek_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short) < + subghz_protocol_holtek_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = HoltekDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = HoltekDecoderStepReset; + } + } else { + instance->decoder.parser_step = HoltekDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_holtek_check_remote_controller(SubGhzBlockGeneric* instance) { + if((instance->data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) { + instance->serial = + subghz_protocol_blocks_reverse_key((instance->data >> 16) & 0xFFFFF, 20); + uint16_t btn = instance->data & 0xFFFF; + if((btn & 0xf) != 0xA) { + instance->btn = 0x1 << 4 | (btn & 0xF); + } else if(((btn >> 4) & 0xF) != 0xA) { + instance->btn = 0x2 << 4 | ((btn >> 4) & 0xF); + } else if(((btn >> 8) & 0xF) != 0xA) { + instance->btn = 0x3 << 4 | ((btn >> 8) & 0xF); + } else if(((btn >> 12) & 0xF) != 0xA) { + instance->btn = 0x4 << 4 | ((btn >> 12) & 0xF); + } else { + instance->btn = 0; + } + } else { + instance->serial = 0; + instance->btn = 0; + instance->cnt = 0; + } +} + +uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_holtek_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_holtek_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_holtek_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + subghz_protocol_holtek_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%05lX Btn:%X ", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF), + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + instance->generic.btn >> 4); + + if((instance->generic.btn & 0xF) == 0xE) { + furi_string_cat_printf(output, "ON\r\n"); + } else if(((instance->generic.btn & 0xF) == 0xB)) { + furi_string_cat_printf(output, "OFF\r\n"); + } +} diff --git a/applications/main/subghz/protocols/holtek.h b/applications/main/subghz/protocols/holtek.h new file mode 100644 index 000000000..252a26dc7 --- /dev/null +++ b/applications/main/subghz/protocols/holtek.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HOLTEK_NAME "Holtek" + +typedef struct SubGhzProtocolDecoderHoltek SubGhzProtocolDecoderHoltek; +typedef struct SubGhzProtocolEncoderHoltek SubGhzProtocolEncoderHoltek; + +extern const SubGhzProtocolDecoder subghz_protocol_holtek_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_holtek_encoder; +extern const SubGhzProtocol subghz_protocol_holtek; + +/** + * Allocate SubGhzProtocolEncoderHoltek. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHoltek* pointer to a SubGhzProtocolEncoderHoltek instance + */ +void* subghz_protocol_encoder_holtek_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHoltek. + * @param context Pointer to a SubGhzProtocolEncoderHoltek instance + */ +void subghz_protocol_encoder_holtek_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHoltek instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHoltek instance + */ +void subghz_protocol_encoder_holtek_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHoltek instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_holtek_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHoltek. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHoltek* pointer to a SubGhzProtocolDecoderHoltek instance + */ +void* subghz_protocol_decoder_holtek_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHoltek. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + */ +void subghz_protocol_decoder_holtek_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHoltek. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + */ +void subghz_protocol_decoder_holtek_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHoltek. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_holtek_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHoltek. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + * @param output Resulting text + */ +void subghz_protocol_decoder_holtek_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/holtek_ht12x.c b/applications/main/subghz/protocols/holtek_ht12x.c new file mode 100644 index 000000000..169387ded --- /dev/null +++ b/applications/main/subghz/protocols/holtek_ht12x.c @@ -0,0 +1,400 @@ +#include "holtek_ht12x.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://www.holtek.com/documents/10179/116711/HT12A_Ev130.pdf + * + */ + +#define TAG "SubGhzProtocolHoltek_HT12X" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0080 ? '0' : '1'), (dip & 0x0040 ? '0' : '1'), (dip & 0x0020 ? '0' : '1'), \ + (dip & 0x0010 ? '0' : '1'), (dip & 0x0008 ? '0' : '1'), (dip & 0x0004 ? '0' : '1'), \ + (dip & 0x0002 ? '0' : '1'), (dip & 0x0001 ? '0' : '1') + +static const SubGhzBlockConst subghz_protocol_holtek_th12x_const = { + .te_short = 320, + .te_long = 640, + .te_delta = 200, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderHoltek_HT12X { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t te; + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderHoltek_HT12X { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint32_t te; +}; + +typedef enum { + Holtek_HT12XDecoderStepReset = 0, + Holtek_HT12XDecoderStepFoundStartBit, + Holtek_HT12XDecoderStepSaveDuration, + Holtek_HT12XDecoderStepCheckDuration, +} Holtek_HT12XDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder = { + .alloc = subghz_protocol_decoder_holtek_th12x_alloc, + .free = subghz_protocol_decoder_holtek_th12x_free, + + .feed = subghz_protocol_decoder_holtek_th12x_feed, + .reset = subghz_protocol_decoder_holtek_th12x_reset, + + .get_hash_data = subghz_protocol_decoder_holtek_th12x_get_hash_data, + .serialize = subghz_protocol_decoder_holtek_th12x_serialize, + .deserialize = subghz_protocol_decoder_holtek_th12x_deserialize, + .get_string = subghz_protocol_decoder_holtek_th12x_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder = { + .alloc = subghz_protocol_encoder_holtek_th12x_alloc, + .free = subghz_protocol_encoder_holtek_th12x_free, + + .deserialize = subghz_protocol_encoder_holtek_th12x_deserialize, + .stop = subghz_protocol_encoder_holtek_th12x_stop, + .yield = subghz_protocol_encoder_holtek_th12x_yield, +}; + +const SubGhzProtocol subghz_protocol_holtek_th12x = { + .name = SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_holtek_th12x_decoder, + .encoder = &subghz_protocol_holtek_th12x_encoder, +}; + +void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHoltek_HT12X* instance = + malloc(sizeof(SubGhzProtocolEncoderHoltek_HT12X)); + + instance->base.protocol = &subghz_protocol_holtek_th12x; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_holtek_th12x_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @return true On success + */ +static bool + subghz_protocol_encoder_holtek_th12x_get_upload(SubGhzProtocolEncoderHoltek_HT12X* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 36); + //Send start bit + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)instance->te * 2); + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)instance->te * 2); + } + } + return true; +} + +bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_holtek_th12x_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_holtek_th12x_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_holtek_th12x_stop(void* context) { + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context) { + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHoltek_HT12X* instance = + malloc(sizeof(SubGhzProtocolDecoderHoltek_HT12X)); + instance->base.protocol = &subghz_protocol_holtek_th12x; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_holtek_th12x_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + free(instance); +} + +void subghz_protocol_decoder_holtek_th12x_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; +} + +void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + + switch(instance->decoder.parser_step) { + case Holtek_HT12XDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short * 36) < + subghz_protocol_holtek_th12x_const.te_delta * 36)) { + //Found Preambula + instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit; + } + break; + case Holtek_HT12XDecoderStepFoundStartBit: + if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta)) { + //Found StartBit + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = duration; + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + case Holtek_HT12XDecoderStepSaveDuration: + //save duration + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_holtek_th12x_const.te_short * 10 + + subghz_protocol_holtek_th12x_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_holtek_th12x_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->te /= (instance->decoder.decode_count_bit * 3 + 1); + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->last_data = instance->decoder.decode_data; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit; + break; + } else { + instance->decoder.te_last = duration; + instance->te += duration; + instance->decoder.parser_step = Holtek_HT12XDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + case Holtek_HT12XDecoderStepCheckDuration: + if(level) { + instance->te += duration; + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_long) < + subghz_protocol_holtek_th12x_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_long) < + subghz_protocol_holtek_th12x_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_holtek_th12x_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = instance->data & 0x0F; + instance->cnt = (instance->data >> 4) & 0xFF; +} + +uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_holtek_th12x_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_holtek_th12x_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + ret = true; + } while(false); + return ret; +} + +static void subghz_protocol_holtek_th12x_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s\r\n", + (((event >> 3) & 0x1) == 0x0 ? "B1 " : ""), + (((event >> 2) & 0x1) == 0x0 ? "B2 " : ""), + (((event >> 1) & 0x1) == 0x0 ? "B3 " : ""), + (((event >> 0) & 0x1) == 0x0 ? "B4 " : "")); +} + +void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + subghz_protocol_holtek_th12x_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%03lX\r\n" + "Btn: ", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFF)); + subghz_protocol_holtek_th12x_event_serialize(instance->generic.btn, output); + furi_string_cat_printf( + output, + "DIP:" DIP_PATTERN "\r\n" + "Te:%luus\r\n", + CNT_TO_DIP(instance->generic.cnt), + instance->te); +} diff --git a/applications/main/subghz/protocols/holtek_ht12x.h b/applications/main/subghz/protocols/holtek_ht12x.h new file mode 100644 index 000000000..7b5c31dd7 --- /dev/null +++ b/applications/main/subghz/protocols/holtek_ht12x.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME "Holtek_HT12X" + +typedef struct SubGhzProtocolDecoderHoltek_HT12X SubGhzProtocolDecoderHoltek_HT12X; +typedef struct SubGhzProtocolEncoderHoltek_HT12X SubGhzProtocolEncoderHoltek_HT12X; + +extern const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder; +extern const SubGhzProtocol subghz_protocol_holtek_th12x; + +/** + * Allocate SubGhzProtocolEncoderHoltek_HT12X. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHoltek_HT12X* pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void subghz_protocol_encoder_holtek_th12x_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void subghz_protocol_encoder_holtek_th12x_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHoltek_HT12X. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHoltek_HT12X* pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void subghz_protocol_decoder_holtek_th12x_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void subghz_protocol_decoder_holtek_th12x_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_holtek_th12x_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param output Resulting text + */ +void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/honeywell_wdb.c b/applications/main/subghz/protocols/honeywell_wdb.c new file mode 100644 index 000000000..3b940fc67 --- /dev/null +++ b/applications/main/subghz/protocols/honeywell_wdb.c @@ -0,0 +1,399 @@ +#include "honeywell_wdb.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolHoneywellWDB" + +/* + * + * https://github.com/klohner/honeywell-wireless-doorbell + * + */ + +static const SubGhzBlockConst subghz_protocol_honeywell_wdb_const = { + .te_short = 160, + .te_long = 320, + .te_delta = 60, + .min_count_bit_for_found = 48, +}; + +struct SubGhzProtocolDecoderHoneywell_WDB { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + const char* device_type; + const char* alert; + uint8_t secret_knock; + uint8_t relay; + uint8_t lowbat; +}; + +struct SubGhzProtocolEncoderHoneywell_WDB { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Honeywell_WDBDecoderStepReset = 0, + Honeywell_WDBDecoderStepFoundStartBit, + Honeywell_WDBDecoderStepSaveDuration, + Honeywell_WDBDecoderStepCheckDuration, +} Honeywell_WDBDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_honeywell_wdb_decoder = { + .alloc = subghz_protocol_decoder_honeywell_wdb_alloc, + .free = subghz_protocol_decoder_honeywell_wdb_free, + + .feed = subghz_protocol_decoder_honeywell_wdb_feed, + .reset = subghz_protocol_decoder_honeywell_wdb_reset, + + .get_hash_data = subghz_protocol_decoder_honeywell_wdb_get_hash_data, + .serialize = subghz_protocol_decoder_honeywell_wdb_serialize, + .deserialize = subghz_protocol_decoder_honeywell_wdb_deserialize, + .get_string = subghz_protocol_decoder_honeywell_wdb_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_honeywell_wdb_encoder = { + .alloc = subghz_protocol_encoder_honeywell_wdb_alloc, + .free = subghz_protocol_encoder_honeywell_wdb_free, + + .deserialize = subghz_protocol_encoder_honeywell_wdb_deserialize, + .stop = subghz_protocol_encoder_honeywell_wdb_stop, + .yield = subghz_protocol_encoder_honeywell_wdb_yield, +}; + +const SubGhzProtocol subghz_protocol_honeywell_wdb = { + .name = SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_honeywell_wdb_decoder, + .encoder = &subghz_protocol_honeywell_wdb_encoder, +}; + +void* subghz_protocol_encoder_honeywell_wdb_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHoneywell_WDB* instance = + malloc(sizeof(SubGhzProtocolEncoderHoneywell_WDB)); + + instance->base.protocol = &subghz_protocol_honeywell_wdb; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_honeywell_wdb_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHoneywell_WDB* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + * @return true On success + */ +static bool subghz_protocol_encoder_honeywell_wdb_get_upload( + SubGhzProtocolEncoderHoneywell_WDB* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short * 3); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_long); + } + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short * 3); + return true; +} + +bool subghz_protocol_encoder_honeywell_wdb_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHoneywell_WDB* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_honeywell_wdb_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_honeywell_wdb_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_honeywell_wdb_stop(void* context) { + SubGhzProtocolEncoderHoneywell_WDB* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_honeywell_wdb_yield(void* context) { + SubGhzProtocolEncoderHoneywell_WDB* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_honeywell_wdb_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHoneywell_WDB* instance = + malloc(sizeof(SubGhzProtocolDecoderHoneywell_WDB)); + instance->base.protocol = &subghz_protocol_honeywell_wdb; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_honeywell_wdb_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + free(instance); +} + +void subghz_protocol_decoder_honeywell_wdb_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; +} + +void subghz_protocol_decoder_honeywell_wdb_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + switch(instance->decoder.parser_step) { + case Honeywell_WDBDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short * 3) < + subghz_protocol_honeywell_wdb_const.te_delta)) { + //Found header Honeywell_WDB + instance->decoder.decode_count_bit = 0; + instance->decoder.decode_data = 0; + instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration; + } + break; + case Honeywell_WDBDecoderStepSaveDuration: + if(level) { //save interval + if(DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short * 3) < + subghz_protocol_honeywell_wdb_const.te_delta) { + if((instance->decoder.decode_count_bit == + subghz_protocol_honeywell_wdb_const.min_count_bit_for_found) && + ((instance->decoder.decode_data & 0x01) == + subghz_protocol_blocks_get_parity( + instance->decoder.decode_data >> 1, + subghz_protocol_honeywell_wdb_const.min_count_bit_for_found - 1))) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = Honeywell_WDBDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; + } + break; + case Honeywell_WDBDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_honeywell_wdb_const.te_short) < + subghz_protocol_honeywell_wdb_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_long) < + subghz_protocol_honeywell_wdb_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_honeywell_wdb_const.te_long) < + subghz_protocol_honeywell_wdb_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short) < + subghz_protocol_honeywell_wdb_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration; + } else + instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; + } else { + instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzProtocolDecoderHoneywell_WDB* instance + */ +static void subghz_protocol_honeywell_wdb_check_remote_controller( + SubGhzProtocolDecoderHoneywell_WDB* instance) { + /* + * + * Frame bits used in Honeywell RCWL300A, RCWL330A, Series 3, 5, 9 and all Decor Series Wireless Chimes + * 0000 0000 1111 1111 2222 2222 3333 3333 4444 4444 5555 5555 + * 7654 3210 7654 3210 7654 3210 7654 3210 7654 3210 7654 3210 + * XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XX.. XXX. .... KEY DATA (any change and receiver doesn't seem to recognize signal) + * XXXX XXXX XXXX XXXX XXXX .... .... .... .... .... .... .... KEY ID (different for each transmitter) + * .... .... .... .... .... 0000 00.. 0000 0000 00.. 000. .... KEY UNKNOWN 0 (always 0 in devices I've tested) + * .... .... .... .... .... .... ..XX .... .... .... .... .... DEVICE TYPE (10 = doorbell, 01 = PIR Motion sensor) + * .... .... .... .... .... .... .... .... .... ..XX ...X XXX. FLAG DATA (may be modified for possible effects on receiver) + * .... .... .... .... .... .... .... .... .... ..XX .... .... ALERT (00 = normal, 01 or 10 = right-left halo light pattern, 11 = full volume alarm) + * .... .... .... .... .... .... .... .... .... .... ...X .... SECRET KNOCK (0 = default, 1 if doorbell is pressed 3x rapidly) + * .... .... .... .... .... .... .... .... .... .... .... X... RELAY (1 if signal is a retransmission of a received transmission, only some models) + * .... .... .... .... .... .... .... .... .... .... .... .X.. FLAG UNKNOWN (0 = default, but 1 is accepted and I don't observe any effects) + * .... .... .... .... .... .... .... .... .... .... .... ..X. LOWBAT (1 if battery is low, receiver gives low battery alert) + * .... .... .... .... .... .... .... .... .... .... .... ...X PARITY (LSB of count of set bits in previous 47 bits) + * + */ + + instance->generic.serial = (instance->generic.data >> 28) & 0xFFFFF; + switch((instance->generic.data >> 20) & 0x3) { + case 0x02: + instance->device_type = "Doorbell"; + break; + case 0x01: + instance->device_type = "PIR-Motion"; + break; + default: + instance->device_type = "Unknown"; + break; + } + + switch((instance->generic.data >> 16) & 0x3) { + case 0x00: + instance->alert = "Normal"; + break; + case 0x01: + case 0x02: + instance->alert = "High"; + break; + case 0x03: + instance->alert = "Full"; + break; + default: + instance->alert = "Unknown"; + break; + } + + instance->secret_knock = (uint8_t)((instance->generic.data >> 4) & 0x1); + instance->relay = (uint8_t)((instance->generic.data >> 3) & 0x1); + instance->lowbat = (uint8_t)((instance->generic.data >> 1) & 0x1); +} + +uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_honeywell_wdb_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_honeywell_wdb_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_honeywell_wdb_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_honeywell_wdb_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + subghz_protocol_honeywell_wdb_check_remote_controller(instance); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%05lX\r\n" + "DT:%s Al:%s\r\n" + "SK:%01X R:%01X LBat:%01X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF), + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + instance->device_type, + instance->alert, + instance->secret_knock, + instance->relay, + instance->lowbat); +} diff --git a/applications/main/subghz/protocols/honeywell_wdb.h b/applications/main/subghz/protocols/honeywell_wdb.h new file mode 100644 index 000000000..828631837 --- /dev/null +++ b/applications/main/subghz/protocols/honeywell_wdb.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME "Honeywell" + +typedef struct SubGhzProtocolDecoderHoneywell_WDB SubGhzProtocolDecoderHoneywell_WDB; +typedef struct SubGhzProtocolEncoderHoneywell_WDB SubGhzProtocolEncoderHoneywell_WDB; + +extern const SubGhzProtocolDecoder subghz_protocol_honeywell_wdb_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_honeywell_wdb_encoder; +extern const SubGhzProtocol subghz_protocol_honeywell_wdb; + +/** + * Allocate SubGhzProtocolEncoderHoneywell_WDB. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHoneywell_WDB* pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + */ +void* subghz_protocol_encoder_honeywell_wdb_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHoneywell_WDB. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + */ +void subghz_protocol_encoder_honeywell_wdb_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_honeywell_wdb_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + */ +void subghz_protocol_encoder_honeywell_wdb_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_honeywell_wdb_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHoneywell_WDB. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHoneywell_WDB* pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + */ +void* subghz_protocol_decoder_honeywell_wdb_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHoneywell_WDB. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + */ +void subghz_protocol_decoder_honeywell_wdb_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHoneywell_WDB. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + */ +void subghz_protocol_decoder_honeywell_wdb_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_honeywell_wdb_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHoneywell_WDB. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_honeywell_wdb_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHoneywell_WDB. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_honeywell_wdb_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + * @param output Resulting text + */ +void subghz_protocol_decoder_honeywell_wdb_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/hormann.c b/applications/main/subghz/protocols/hormann.c new file mode 100644 index 000000000..67b8cdfca --- /dev/null +++ b/applications/main/subghz/protocols/hormann.c @@ -0,0 +1,341 @@ +#include "hormann.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolHormannHSM" + +#define HORMANN_HSM_PATTERN 0xFF000000003 + +static const SubGhzBlockConst subghz_protocol_hormann_const = { + .te_short = 500, + .te_long = 1000, + .te_delta = 200, + .min_count_bit_for_found = 44, +}; + +struct SubGhzProtocolDecoderHormann { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderHormann { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + HormannDecoderStepReset = 0, + HormannDecoderStepFoundStartHeader, + HormannDecoderStepFoundHeader, + HormannDecoderStepFoundStartBit, + HormannDecoderStepSaveDuration, + HormannDecoderStepCheckDuration, +} HormannDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_hormann_decoder = { + .alloc = subghz_protocol_decoder_hormann_alloc, + .free = subghz_protocol_decoder_hormann_free, + + .feed = subghz_protocol_decoder_hormann_feed, + .reset = subghz_protocol_decoder_hormann_reset, + + .get_hash_data = subghz_protocol_decoder_hormann_get_hash_data, + .serialize = subghz_protocol_decoder_hormann_serialize, + .deserialize = subghz_protocol_decoder_hormann_deserialize, + .get_string = subghz_protocol_decoder_hormann_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_hormann_encoder = { + .alloc = subghz_protocol_encoder_hormann_alloc, + .free = subghz_protocol_encoder_hormann_free, + + .deserialize = subghz_protocol_encoder_hormann_deserialize, + .stop = subghz_protocol_encoder_hormann_stop, + .yield = subghz_protocol_encoder_hormann_yield, +}; + +const SubGhzProtocol subghz_protocol_hormann = { + .name = SUBGHZ_PROTOCOL_HORMANN_HSM_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_hormann_decoder, + .encoder = &subghz_protocol_hormann_encoder, +}; + +void* subghz_protocol_encoder_hormann_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHormann* instance = malloc(sizeof(SubGhzProtocolEncoderHormann)); + + instance->base.protocol = &subghz_protocol_hormann; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 2048; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_hormann_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHormann* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHormann instance + * @return true On success + */ +static bool subghz_protocol_encoder_hormann_get_upload(SubGhzProtocolEncoderHormann* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2 + 2) * 20 + 1; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + instance->encoder.repeat = 10; //original remote does 10 repeats + + for(size_t repeat = 0; repeat < 20; repeat++) { + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 24); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_long); + } + } + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 24); + return true; +} + +bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHormann* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_hormann_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_hormann_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_hormann_stop(void* context) { + SubGhzProtocolEncoderHormann* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_hormann_yield(void* context) { + SubGhzProtocolEncoderHormann* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_hormann_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHormann* instance = malloc(sizeof(SubGhzProtocolDecoderHormann)); + instance->base.protocol = &subghz_protocol_hormann; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_hormann_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + free(instance); +} + +static bool subghz_protocol_decoder_hormann_check_pattern(SubGhzProtocolDecoderHormann* instance) { + return (instance->decoder.decode_data & HORMANN_HSM_PATTERN) == HORMANN_HSM_PATTERN; +} + +void subghz_protocol_decoder_hormann_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + instance->decoder.parser_step = HormannDecoderStepReset; +} + +void subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + + switch(instance->decoder.parser_step) { + case HormannDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short * 24) < + subghz_protocol_hormann_const.te_delta * 24)) { + instance->decoder.parser_step = HormannDecoderStepFoundStartBit; + } + break; + case HormannDecoderStepFoundStartBit: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short) < + subghz_protocol_hormann_const.te_delta)) { + instance->decoder.parser_step = HormannDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = HormannDecoderStepReset; + } + break; + case HormannDecoderStepSaveDuration: + if(level) { //save interval + if(duration >= (subghz_protocol_hormann_const.te_short * 5) && + subghz_protocol_decoder_hormann_check_pattern(instance)) { + instance->decoder.parser_step = HormannDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_hormann_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = HormannDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = HormannDecoderStepReset; + } + break; + case HormannDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hormann_const.te_short) < + subghz_protocol_hormann_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_long) < + subghz_protocol_hormann_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = HormannDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hormann_const.te_long) < + subghz_protocol_hormann_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short) < + subghz_protocol_hormann_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = HormannDecoderStepSaveDuration; + } else + instance->decoder.parser_step = HormannDecoderStepReset; + } else { + instance->decoder.parser_step = HormannDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_hormann_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = (instance->data >> 4) & 0xF; +} + +uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_hormann_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_hormann_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_hormann_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + subghz_protocol_hormann_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s\r\n" + "%dbit\r\n" + "Key:0x%03lX%08lX\r\n" + "Btn:0x%01X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/hormann.h b/applications/main/subghz/protocols/hormann.h new file mode 100644 index 000000000..857a50041 --- /dev/null +++ b/applications/main/subghz/protocols/hormann.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HORMANN_HSM_NAME "Hormann HSM" + +typedef struct SubGhzProtocolDecoderHormann SubGhzProtocolDecoderHormann; +typedef struct SubGhzProtocolEncoderHormann SubGhzProtocolEncoderHormann; + +extern const SubGhzProtocolDecoder subghz_protocol_hormann_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_hormann_encoder; +extern const SubGhzProtocol subghz_protocol_hormann; + +/** + * Allocate SubGhzProtocolEncoderHormann. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHormann* pointer to a SubGhzProtocolEncoderHormann instance + */ +void* subghz_protocol_encoder_hormann_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHormann. + * @param context Pointer to a SubGhzProtocolEncoderHormann instance + */ +void subghz_protocol_encoder_hormann_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHormann instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHormann instance + */ +void subghz_protocol_encoder_hormann_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHormann instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_hormann_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHormann. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHormann* pointer to a SubGhzProtocolDecoderHormann instance + */ +void* subghz_protocol_decoder_hormann_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHormann. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + */ +void subghz_protocol_decoder_hormann_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHormann. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + */ +void subghz_protocol_decoder_hormann_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHormann. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_hormann_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHormann. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + * @param output Resulting text + */ +void subghz_protocol_decoder_hormann_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/ido.c b/applications/main/subghz/protocols/ido.c new file mode 100644 index 000000000..dff9defc0 --- /dev/null +++ b/applications/main/subghz/protocols/ido.c @@ -0,0 +1,234 @@ +#include "ido.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocol_iDo_117/111" + +static const SubGhzBlockConst subghz_protocol_ido_const = { + .te_short = 450, + .te_long = 1450, + .te_delta = 150, + .min_count_bit_for_found = 48, +}; + +struct SubGhzProtocolDecoderIDo { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderIDo { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + IDoDecoderStepReset = 0, + IDoDecoderStepFoundPreambula, + IDoDecoderStepSaveDuration, + IDoDecoderStepCheckDuration, +} IDoDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_ido_decoder = { + .alloc = subghz_protocol_decoder_ido_alloc, + .free = subghz_protocol_decoder_ido_free, + + .feed = subghz_protocol_decoder_ido_feed, + .reset = subghz_protocol_decoder_ido_reset, + + .get_hash_data = subghz_protocol_decoder_ido_get_hash_data, + .deserialize = subghz_protocol_decoder_ido_deserialize, + .serialize = subghz_protocol_decoder_ido_serialize, + .get_string = subghz_protocol_decoder_ido_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_ido_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_ido = { + .name = SUBGHZ_PROTOCOL_IDO_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Save, + + .decoder = &subghz_protocol_ido_decoder, + .encoder = &subghz_protocol_ido_encoder, +}; + +void* subghz_protocol_decoder_ido_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderIDo* instance = malloc(sizeof(SubGhzProtocolDecoderIDo)); + instance->base.protocol = &subghz_protocol_ido; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_ido_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + free(instance); +} + +void subghz_protocol_decoder_ido_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + instance->decoder.parser_step = IDoDecoderStepReset; +} + +void subghz_protocol_decoder_ido_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + + switch(instance->decoder.parser_step) { + case IDoDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short * 10) < + subghz_protocol_ido_const.te_delta * 5)) { + instance->decoder.parser_step = IDoDecoderStepFoundPreambula; + } + break; + case IDoDecoderStepFoundPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short * 10) < + subghz_protocol_ido_const.te_delta * 5)) { + //Found Preambula + instance->decoder.parser_step = IDoDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = IDoDecoderStepReset; + } + break; + case IDoDecoderStepSaveDuration: + if(level) { + if(duration >= ((uint32_t)subghz_protocol_ido_const.te_short * 5 + + subghz_protocol_ido_const.te_delta)) { + instance->decoder.parser_step = IDoDecoderStepFoundPreambula; + if(instance->decoder.decode_count_bit >= + subghz_protocol_ido_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = IDoDecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = IDoDecoderStepReset; + } + break; + case IDoDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ido_const.te_short) < + subghz_protocol_ido_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ido_const.te_long) < + subghz_protocol_ido_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = IDoDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ido_const.te_short) < + subghz_protocol_ido_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short) < + subghz_protocol_ido_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = IDoDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = IDoDecoderStepReset; + } + } else { + instance->decoder.parser_step = IDoDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_ido_check_remote_controller(SubGhzBlockGeneric* instance) { + uint64_t code_found_reverse = + subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); + uint32_t code_fix = code_found_reverse & 0xFFFFFF; + + instance->serial = code_fix & 0xFFFFF; + instance->btn = (code_fix >> 20) & 0x0F; +} + +uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_ido_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != subghz_protocol_ido_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_ido_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + + subghz_protocol_ido_check_remote_controller(&instance->generic); + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + uint32_t code_fix = code_found_reverse & 0xFFFFFF; + uint32_t code_hop = (code_found_reverse >> 24) & 0xFFFFFF; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Fix:%06lX \r\n" + "Hop:%06lX \r\n" + "Sn:%05lX Btn:%X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + code_fix, + code_hop, + instance->generic.serial, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/ido.h b/applications/main/subghz/protocols/ido.h new file mode 100644 index 000000000..634f6ff89 --- /dev/null +++ b/applications/main/subghz/protocols/ido.h @@ -0,0 +1,73 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_IDO_NAME "iDo 117/111" + +typedef struct SubGhzProtocolDecoderIDo SubGhzProtocolDecoderIDo; +typedef struct SubGhzProtocolEncoderIDo SubGhzProtocolEncoderIDo; + +extern const SubGhzProtocolDecoder subghz_protocol_ido_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_ido_encoder; +extern const SubGhzProtocol subghz_protocol_ido; + +/** + * Allocate SubGhzProtocolDecoderIDo. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderIDo* pointer to a SubGhzProtocolDecoderIDo instance + */ +void* subghz_protocol_decoder_ido_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderIDo. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + */ +void subghz_protocol_decoder_ido_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderIDo. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + */ +void subghz_protocol_decoder_ido_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_ido_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderIDo. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_ido_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderIDo. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + * @param output Resulting text + */ +void subghz_protocol_decoder_ido_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/intertechno_v3.c b/applications/main/subghz/protocols/intertechno_v3.c new file mode 100644 index 000000000..2c4e514cc --- /dev/null +++ b/applications/main/subghz/protocols/intertechno_v3.c @@ -0,0 +1,472 @@ +#include "intertechno_v3.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolIntertechnoV3" + +#define CH_PATTERN "%c%c%c%c" +#define CNT_TO_CH(ch) \ + (ch & 0x8 ? '1' : '0'), (ch & 0x4 ? '1' : '0'), (ch & 0x2 ? '1' : '0'), (ch & 0x1 ? '1' : '0') + +#define INTERTECHNO_V3_DIMMING_COUNT_BIT 36 + +static const SubGhzBlockConst subghz_protocol_intertechno_v3_const = { + .te_short = 275, + .te_long = 1375, + .te_delta = 150, + .min_count_bit_for_found = 32, +}; + +struct SubGhzProtocolDecoderIntertechno_V3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderIntertechno_V3 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + IntertechnoV3DecoderStepReset = 0, + IntertechnoV3DecoderStepStartSync, + IntertechnoV3DecoderStepFoundSync, + IntertechnoV3DecoderStepStartDuration, + IntertechnoV3DecoderStepSaveDuration, + IntertechnoV3DecoderStepCheckDuration, + IntertechnoV3DecoderStepEndDuration, +} IntertechnoV3DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder = { + .alloc = subghz_protocol_decoder_intertechno_v3_alloc, + .free = subghz_protocol_decoder_intertechno_v3_free, + + .feed = subghz_protocol_decoder_intertechno_v3_feed, + .reset = subghz_protocol_decoder_intertechno_v3_reset, + + .get_hash_data = subghz_protocol_decoder_intertechno_v3_get_hash_data, + .serialize = subghz_protocol_decoder_intertechno_v3_serialize, + .deserialize = subghz_protocol_decoder_intertechno_v3_deserialize, + .get_string = subghz_protocol_decoder_intertechno_v3_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder = { + .alloc = subghz_protocol_encoder_intertechno_v3_alloc, + .free = subghz_protocol_encoder_intertechno_v3_free, + + .deserialize = subghz_protocol_encoder_intertechno_v3_deserialize, + .stop = subghz_protocol_encoder_intertechno_v3_stop, + .yield = subghz_protocol_encoder_intertechno_v3_yield, +}; + +const SubGhzProtocol subghz_protocol_intertechno_v3 = { + .name = SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_intertechno_v3_decoder, + .encoder = &subghz_protocol_intertechno_v3_encoder, +}; + +void* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderIntertechno_V3* instance = + malloc(sizeof(SubGhzProtocolEncoderIntertechno_V3)); + + instance->base.protocol = &subghz_protocol_intertechno_v3; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_intertechno_v3_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderIntertechno_V3* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + * @return true On success + */ +static bool subghz_protocol_encoder_intertechno_v3_get_upload( + SubGhzProtocolEncoderIntertechno_V3* instance) { + furi_assert(instance); + size_t index = 0; + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 38); + //Send sync + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 10); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if((instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) && (i == 9)) { + //send bit dimm + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + } else if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long); + } + } + instance->encoder.size_upload = index; + return true; +} + +bool subghz_protocol_encoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderIntertechno_V3* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if((instance->generic.data_count_bit != + subghz_protocol_intertechno_v3_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_intertechno_v3_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_intertechno_v3_stop(void* context) { + SubGhzProtocolEncoderIntertechno_V3* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context) { + SubGhzProtocolEncoderIntertechno_V3* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_intertechno_v3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderIntertechno_V3* instance = + malloc(sizeof(SubGhzProtocolDecoderIntertechno_V3)); + instance->base.protocol = &subghz_protocol_intertechno_v3; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_intertechno_v3_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + free(instance); +} + +void subghz_protocol_decoder_intertechno_v3_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; +} + +void subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + switch(instance->decoder.parser_step) { + case IntertechnoV3DecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 37) < + subghz_protocol_intertechno_v3_const.te_delta * 15)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync; + } + break; + case IntertechnoV3DecoderStepStartSync: + if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepFoundSync; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepFoundSync: + if(!level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 10) < + subghz_protocol_intertechno_v3_const.te_delta * 3)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepStartDuration: + if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_intertechno_v3_const.te_short * 11)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync; + if((instance->decoder.decode_count_bit == + subghz_protocol_intertechno_v3_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = IntertechnoV3DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + case IntertechnoV3DecoderStepCheckDuration: + if(level) { + //Add 0 bit + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; + } else if( + //Add 1 bit + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_long) < + subghz_protocol_intertechno_v3_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; + + } else if( + //Add dimm_state + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta) && + (instance->decoder.decode_count_bit == 27)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; + + } else + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepEndDuration: + if(!level && ((DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_long) < + subghz_protocol_intertechno_v3_const.te_delta * 2))) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_intertechno_v3_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * A frame is either 32 or 36 bits: + * + * _ + * start bit: | |__________ (T,10T) + * _ _ + * '0': | |_| |_____ (T,T,T,5T) + * _ _ + * '1': | |_____| |_ (T,5T,T,T) + * _ _ + * dimm: | |_| |_ (T,T,T,T) + * + * _ + * stop bit: | |____...____ (T,38T) + * + * if frame 32 bits + * SSSSSSSSSSSSSSSSSSSSSSSSSS all_ch on/off ~ch + * Key:0x3F86C59F => 00111111100001101100010110 0 1 1111 + * + * if frame 36 bits + * SSSSSSSSSSSSSSSSSSSSSSSSSS all_ch dimm ~ch dimm_level + * Key:0x42D2E8856 => 01000010110100101110100010 0 X 0101 0110 + * + */ + + if(instance->data_count_bit == subghz_protocol_intertechno_v3_const.min_count_bit_for_found) { + instance->serial = (instance->data >> 6) & 0x3FFFFFF; + if((instance->data >> 5) & 0x1) { + instance->cnt = 1 << 5; + } else { + instance->cnt = (~instance->data & 0xF); + } + instance->btn = (instance->data >> 4) & 0x1; + } else if(instance->data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) { + instance->serial = (instance->data >> 10) & 0x3FFFFFF; + if((instance->data >> 9) & 0x1) { + instance->cnt = 1 << 5; + } else { + instance->cnt = (~(instance->data >> 4) & 0xF); + } + instance->btn = (instance->data) & 0xF; + } else { + instance->serial = 0; + instance->cnt = 0; + instance->btn = 0; + } +} + +uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_intertechno_v3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if((instance->generic.data_count_bit != + subghz_protocol_intertechno_v3_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + + subghz_protocol_intertechno_v3_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%.11s %db\r\n" + "Key:0x%08llX\r\n" + "Sn:%07lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial); + + if(instance->generic.data_count_bit == + subghz_protocol_intertechno_v3_const.min_count_bit_for_found) { + if(instance->generic.cnt >> 5) { + furi_string_cat_printf( + output, "Ch: All Btn:%s\r\n", (instance->generic.btn ? "On" : "Off")); + } else { + furi_string_cat_printf( + output, + "Ch:" CH_PATTERN " Btn:%s\r\n", + CNT_TO_CH(instance->generic.cnt), + (instance->generic.btn ? "On" : "Off")); + } + } else if(instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) { + furi_string_cat_printf( + output, + "Ch:" CH_PATTERN " Dimm:%d%%\r\n", + CNT_TO_CH(instance->generic.cnt), + (int)(6.67 * (float)instance->generic.btn)); + } +} diff --git a/applications/main/subghz/protocols/intertechno_v3.h b/applications/main/subghz/protocols/intertechno_v3.h new file mode 100644 index 000000000..ffee14b04 --- /dev/null +++ b/applications/main/subghz/protocols/intertechno_v3.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME "Intertechno_V3" + +typedef struct SubGhzProtocolDecoderIntertechno_V3 SubGhzProtocolDecoderIntertechno_V3; +typedef struct SubGhzProtocolEncoderIntertechno_V3 SubGhzProtocolEncoderIntertechno_V3; + +extern const SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder; +extern const SubGhzProtocol subghz_protocol_intertechno_v3; + +/** + * Allocate SubGhzProtocolEncoderIntertechno_V3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderIntertechno_V3* pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + */ +void* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + */ +void subghz_protocol_encoder_intertechno_v3_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + */ +void subghz_protocol_encoder_intertechno_v3_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderIntertechno_V3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderIntertechno_V3* pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + */ +void* subghz_protocol_decoder_intertechno_v3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + */ +void subghz_protocol_decoder_intertechno_v3_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + */ +void subghz_protocol_decoder_intertechno_v3_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_intertechno_v3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/keeloq.c b/applications/main/subghz/protocols/keeloq.c new file mode 100644 index 000000000..a0970de4d --- /dev/null +++ b/applications/main/subghz/protocols/keeloq.c @@ -0,0 +1,1115 @@ +#include "keeloq.h" +#include "keeloq_common.h" + +#include "../subghz_keystore.h" +#include + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolKeeloq" + +static const SubGhzBlockConst subghz_protocol_keeloq_const = { + .te_short = 400, + .te_long = 800, + .te_delta = 140, + .min_count_bit_for_found = 64, +}; + +struct SubGhzProtocolDecoderKeeloq { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + SubGhzKeystore* keystore; + const char* manufacture_name; + + FuriString* manufacture_from_file; +}; + +struct SubGhzProtocolEncoderKeeloq { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + SubGhzKeystore* keystore; + const char* manufacture_name; + + FuriString* manufacture_from_file; +}; + +typedef enum { + KeeloqDecoderStepReset = 0, + KeeloqDecoderStepCheckPreambula, + KeeloqDecoderStepSaveDuration, + KeeloqDecoderStepCheckDuration, +} KeeloqDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_keeloq_decoder = { + .alloc = subghz_protocol_decoder_keeloq_alloc, + .free = subghz_protocol_decoder_keeloq_free, + + .feed = subghz_protocol_decoder_keeloq_feed, + .reset = subghz_protocol_decoder_keeloq_reset, + + .get_hash_data = subghz_protocol_decoder_keeloq_get_hash_data, + .serialize = subghz_protocol_decoder_keeloq_serialize, + .deserialize = subghz_protocol_decoder_keeloq_deserialize, + .get_string = subghz_protocol_decoder_keeloq_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_keeloq_encoder = { + .alloc = subghz_protocol_encoder_keeloq_alloc, + .free = subghz_protocol_encoder_keeloq_free, + + .deserialize = subghz_protocol_encoder_keeloq_deserialize, + .stop = subghz_protocol_encoder_keeloq_stop, + .yield = subghz_protocol_encoder_keeloq_yield, +}; + +const SubGhzProtocol subghz_protocol_keeloq = { + .name = SUBGHZ_PROTOCOL_KEELOQ_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_keeloq_decoder, + .encoder = &subghz_protocol_keeloq_encoder, +}; + +static const char* mfname; +static int kl_type; + +void keeloq_reset_mfname() { + mfname = ""; +} + +void keeloq_reset_kl_type() { + kl_type = 0; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + */ +static void subghz_protocol_keeloq_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name); + +void* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderKeeloq* instance = malloc(sizeof(SubGhzProtocolEncoderKeeloq)); + + instance->base.protocol = &subghz_protocol_keeloq; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + + instance->encoder.repeat = 100; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + instance->manufacture_from_file = furi_string_alloc(); + + return instance; +} + +void subghz_protocol_encoder_keeloq_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderKeeloq* instance = context; + furi_string_free(instance->manufacture_from_file); + free(instance->encoder.upload); + free(instance); +} + +/** + * Key generation from simple data + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq* instance + * @param btn Button number, 4 bit + */ +static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + uint32_t fix = (uint32_t)btn << 28 | instance->generic.serial; + uint32_t decrypt = (uint32_t)btn << 28 | + (instance->generic.serial & 0x3FF) + << 16 | //ToDo in some protocols the discriminator is 0 + instance->generic.cnt; + uint32_t hop = 0; + uint64_t man = 0; + uint64_t code_found_reverse; + int res = 0; + if(instance->manufacture_name == 0x0) { + instance->manufacture_name = ""; + } + + // DTM Neo uses 12bit -> simple learning -- FAAC_RC,XT , Mutanco_Mutancode -> 12bit normal learning + if((strcmp(instance->manufacture_name, "DTM_Neo") == 0) || + (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || + (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0)) { + decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | instance->generic.cnt; + } + + // Nice Smilo, MHouse, JCM, Normstahl -> 8bit serial - simple learning + if((strcmp(instance->manufacture_name, "NICE_Smilo") == 0) || + (strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0) || + (strcmp(instance->manufacture_name, "JCM_Tech") == 0) || + (strcmp(instance->manufacture_name, "Normstahl") == 0)) { + decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; + } + + if(strcmp(instance->manufacture_name, "Unknown") == 0) { + code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + hop = code_found_reverse & 0x00000000ffffffff; + } else if(strcmp(instance->manufacture_name, "AN-Motors") == 0) { + hop = (instance->generic.cnt & 0xFF) << 24 | (instance->generic.cnt & 0xFF) << 16 | + (instance->generic.btn & 0xF) << 12 | 0x404; + } else if(strcmp(instance->manufacture_name, "HCS101") == 0) { + hop = instance->generic.cnt << 16 | (instance->generic.btn & 0xF) << 12 | 0x000; + } else { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + //Simple Learning + hop = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); + break; + case KEELOQ_LEARNING_NORMAL: + //Simple Learning + man = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_SECURE: + //Secure Learning + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->generic.seed, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: + //Magic XOR type-1 Learning + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + instance->generic.serial, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: + //Magic Serial Type 1 learning + man = subghz_protocol_keeloq_common_magic_serial_type1_learning( + fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_UNKNOWN: + if(kl_type == 1) { + hop = + subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); + } + if(kl_type == 2) { + man = subghz_protocol_keeloq_common_normal_learning( + fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + if(kl_type == 3) { + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->generic.seed, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + if(kl_type == 4) { + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + instance->generic.serial, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + break; + } + break; + } + } + } + if(hop) { + uint64_t yek = (uint64_t)fix << 32 | hop; + instance->generic.data = + subghz_protocol_blocks_reverse_key(yek, instance->generic.data_count_bit); + } + return true; +} + +bool subghz_protocol_keeloq_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + const char* manufacture_name, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderKeeloq* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->manufacture_name = manufacture_name; + instance->generic.data_count_bit = 64; + bool res = subghz_protocol_keeloq_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +bool subghz_protocol_keeloq_bft_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + uint32_t seed, + const char* manufacture_name, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderKeeloq* instance = context; + instance->generic.serial = serial; + instance->generic.btn = btn; + instance->generic.cnt = cnt; + instance->generic.seed = seed; + instance->manufacture_name = manufacture_name; + instance->generic.data_count_bit = 64; + // roguuemaster don't steal.!!!! + bool res = subghz_protocol_keeloq_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool + subghz_protocol_encoder_keeloq_get_upload(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_keeloq_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + size_t size_upload = 11 * 2 + 2 + (instance->generic.data_count_bit * 2) + 4; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + for(uint8_t i = 11; i > 0; i--) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short); + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short * 10); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short); + } + } + // +send 2 status bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_long); + // send end + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short * 40); + + return true; +} + +bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderKeeloq* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_keeloq_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_D(TAG, "ENCODER: Missing Seed"); + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + + // Read manufacturer from file + if(flipper_format_read_string( + flipper_format, "Manufacture", instance->manufacture_from_file)) { + instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); + mfname = furi_string_get_cstr(instance->manufacture_from_file); + } else { + FURI_LOG_D(TAG, "ENCODER: Missing Manufacture"); + } + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + subghz_protocol_keeloq_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_keeloq_get_upload(instance, instance->generic.btn)) break; + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_keeloq_stop(void* context) { + SubGhzProtocolEncoderKeeloq* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_keeloq_yield(void* context) { + SubGhzProtocolEncoderKeeloq* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_keeloq_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderKeeloq* instance = malloc(sizeof(SubGhzProtocolDecoderKeeloq)); + instance->base.protocol = &subghz_protocol_keeloq; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + instance->manufacture_from_file = furi_string_alloc(); + + return instance; +} + +void subghz_protocol_decoder_keeloq_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + furi_string_free(instance->manufacture_from_file); + + free(instance); +} + +void subghz_protocol_decoder_keeloq_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + instance->decoder.parser_step = KeeloqDecoderStepReset; + mfname = ""; + kl_type = 0; +} + +void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + + switch(instance->decoder.parser_step) { + case KeeloqDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) < + subghz_protocol_keeloq_const.te_delta) { + instance->decoder.parser_step = KeeloqDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case KeeloqDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) < + subghz_protocol_keeloq_const.te_delta)) { + instance->decoder.parser_step = KeeloqDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short * 10) < + subghz_protocol_keeloq_const.te_delta * 10)) { + // Found header + instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = KeeloqDecoderStepReset; + instance->header_count = 0; + } + break; + case KeeloqDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KeeloqDecoderStepCheckDuration; + } + break; + case KeeloqDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_keeloq_const.te_short * 2 + + subghz_protocol_keeloq_const.te_delta)) { + // Found end TX + instance->decoder.parser_step = KeeloqDecoderStepReset; + if((instance->decoder.decode_count_bit >= + subghz_protocol_keeloq_const.min_count_bit_for_found) && + (instance->decoder.decode_count_bit <= + subghz_protocol_keeloq_const.min_count_bit_for_found + 2)) { + if(instance->generic.data != instance->decoder.decode_data) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = + subghz_protocol_keeloq_const.min_count_bit_for_found; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keeloq_const.te_short) < + subghz_protocol_keeloq_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_long) < + subghz_protocol_keeloq_const.te_delta * 2)) { + if(instance->decoder.decode_count_bit < + subghz_protocol_keeloq_const.min_count_bit_for_found) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.decode_count_bit++; + } + instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keeloq_const.te_long) < + subghz_protocol_keeloq_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) < + subghz_protocol_keeloq_const.te_delta)) { + if(instance->decoder.decode_count_bit < + subghz_protocol_keeloq_const.min_count_bit_for_found) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else { + instance->decoder.decode_count_bit++; + } + instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KeeloqDecoderStepReset; + instance->header_count = 0; + } + } else { + instance->decoder.parser_step = KeeloqDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Validation of decrypt data. + * @param instance Pointer to a SubGhzBlockGeneric instance + * @param decrypt Decrypd data + * @param btn Button number, 4 bit + * @param end_serial decrement the last 10 bits of the serial number + * @return true On success + */ +static inline bool subghz_protocol_keeloq_check_decrypt( + SubGhzBlockGeneric* instance, + uint32_t decrypt, + uint8_t btn, + uint32_t end_serial) { + furi_assert(instance); + if((decrypt >> 28 == btn) && (((((uint16_t)(decrypt >> 16)) & 0xFF) == end_serial) || + ((((uint16_t)(decrypt >> 16)) & 0xFF) == 0))) { + instance->cnt = decrypt & 0x0000FFFF; + return true; + } + return false; +} + +/** + * Checking the accepted code against the database manafacture key + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param fix Fix part of the parcel + * @param hop Hop encrypted part of the parcel + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + * @return true on successful search + */ +static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( + SubGhzBlockGeneric* instance, + uint32_t fix, + uint32_t hop, + SubGhzKeystore* keystore, + const char** manufacture_name) { + // protocol HCS300 uses 10 bits in discriminator, HCS200 uses 8 bits, for backward compatibility, we are looking for the 8-bit pattern + // HCS300 -> uint16_t end_serial = (uint16_t)(fix & 0x3FF); + // HCS200 -> uint16_t end_serial = (uint16_t)(fix & 0xFF); + + uint16_t end_serial = (uint16_t)(fix & 0xFF); + uint8_t btn = (uint8_t)(fix >> 28); + uint32_t decrypt = 0; + uint64_t man; + int res = 0; + if(mfname == 0x0) { + mfname = ""; + } + + if(strcmp(mfname, "") == 0) { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_NORMAL: + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_SECURE: + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: + man = subghz_protocol_keeloq_common_magic_serial_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: + man = subghz_protocol_keeloq_common_magic_serial_type2_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: + man = subghz_protocol_keeloq_common_magic_serial_type3_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_UNKNOWN: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + + // Check for mirrored man + uint64_t man_rev = 0; + uint64_t man_rev_byte = 0; + for(uint8_t i = 0; i < 64; i += 8) { + man_rev_byte = (uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56 - i); + } + + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + + //########################### + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Secure Learning + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 3; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_secure_learning(fix, instance->seed, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 3; + return 1; + } + + // Magic xor type1 learning + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 4; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_magic_xor_type1_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 4; + return 1; + } + + break; + } + } + } else if(strcmp(mfname, "Unknown") == 0) { + return 1; + } else { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_NORMAL: + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_SECURE: + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: + man = subghz_protocol_keeloq_common_magic_serial_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_UNKNOWN: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + // Check for mirrored man + uint64_t man_rev = 0; + uint64_t man_rev_byte = 0; + for(uint8_t i = 0; i < 64; i += 8) { + man_rev_byte = (uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56 - i); + } + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + //########################### + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Secure Learning + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 3; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 3; + return 1; + } + + // Magic xor type1 learning + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 4; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_magic_xor_type1_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 4; + return 1; + } + + break; + } + } + } + } + + *manufacture_name = "Unknown"; + mfname = "Unknown"; + instance->cnt = 0; + + return 0; +} + +static void subghz_protocol_keeloq_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name) { + uint64_t key = subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); + uint32_t key_fix = key >> 32; + uint32_t key_hop = key & 0x00000000ffffffff; + // Check key AN-Motors + if((key_hop >> 24) == ((key_hop >> 16) & 0x00ff) && + (key_fix >> 28) == ((key_hop >> 12) & 0x0f) && (key_hop & 0xFFF) == 0x404) { + *manufacture_name = "AN-Motors"; + mfname = *manufacture_name; + instance->cnt = key_hop >> 16; + } else if((key_hop & 0xFFF) == (0x000) && (key_fix >> 28) == ((key_hop >> 12) & 0x0f)) { + *manufacture_name = "HCS101"; + mfname = *manufacture_name; + instance->cnt = key_hop >> 16; + } else { + subghz_protocol_keeloq_check_remote_controller_selector( + instance, key_fix, key_hop, keystore, manufacture_name); + } + + instance->serial = key_fix & 0x0FFFFFFF; + instance->btn = key_fix >> 28; +} + +uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_keeloq_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + subghz_protocol_keeloq_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + if(strcmp(instance->manufacture_name, "BFT") == 0) { + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(res && !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_E(TAG, "DECODER Serialize: Unable to add Seed"); + res = false; + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + } + + if(res && !flipper_format_write_string_cstr( + flipper_format, "Manufacture", instance->manufacture_name)) { + FURI_LOG_E(TAG, "DECODER Serialize: Unable to add manufacture name"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_keeloq_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_D(TAG, "DECODER: Missing Seed"); + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + + // Read manufacturer from file + if(flipper_format_read_string( + flipper_format, "Manufacture", instance->manufacture_from_file)) { + instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); + mfname = furi_string_get_cstr(instance->manufacture_from_file); + } else { + FURI_LOG_D(TAG, "DECODER: Missing Manufacture"); + } + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + + subghz_protocol_keeloq_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + uint32_t code_found_reverse_hi = code_found_reverse >> 32; + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + if(strcmp(instance->manufacture_name, "BFT") == 0) { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%08lX%08lX\r\n" + "Fix:0x%08lX Cnt:%04lX\r\n" + "Hop:0x%08lX Btn:%01X\r\n" + "MF:%s Sd:%08lX", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + instance->generic.cnt, + code_found_reverse_lo, + instance->generic.btn, + instance->manufacture_name, + instance->generic.seed); + } else { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%08lX%08lX\r\n" + "Fix:0x%08lX Cnt:%04lX\r\n" + "Hop:0x%08lX Btn:%01X\r\n" + "MF:%s", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + instance->generic.cnt, + code_found_reverse_lo, + instance->generic.btn, + instance->manufacture_name); + } +} diff --git a/applications/main/subghz/protocols/keeloq.h b/applications/main/subghz/protocols/keeloq.h new file mode 100644 index 000000000..7b0cfc3bd --- /dev/null +++ b/applications/main/subghz/protocols/keeloq.h @@ -0,0 +1,153 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_KEELOQ_NAME "KeeLoq" + +typedef struct SubGhzProtocolDecoderKeeloq SubGhzProtocolDecoderKeeloq; +typedef struct SubGhzProtocolEncoderKeeloq SubGhzProtocolEncoderKeeloq; + +extern const SubGhzProtocolDecoder subghz_protocol_keeloq_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_keeloq_encoder; +extern const SubGhzProtocol subghz_protocol_keeloq; + +void keeloq_reset_mfname(); + +void keeloq_reset_kl_type(); + +/** + * Allocate SubGhzProtocolEncoderKeeloq. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderKeeloq* pointer to a SubGhzProtocolEncoderKeeloq instance + */ +void* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderKeeloq. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + */ +void subghz_protocol_encoder_keeloq_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 28 bit + * @param btn Button number, 4 bit + * @param cnt Counter value, 16 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_keeloq_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + const char* manufacture_name, + SubGhzRadioPreset* preset); + +/** + * Key generation for BFT. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 28 bit + * @param btn Button number, 4 bit + * @param cnt Counter value, 16 bit + * @param seed Seed value, 32 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_keeloq_bft_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + uint32_t seed, + const char* manufacture_name, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + */ +void subghz_protocol_encoder_keeloq_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_keeloq_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderKeeloq. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderKeeloq* pointer to a SubGhzProtocolDecoderKeeloq instance + */ +void* subghz_protocol_decoder_keeloq_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderKeeloq. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + */ +void subghz_protocol_decoder_keeloq_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderKeeloq. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + */ +void subghz_protocol_decoder_keeloq_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderKeeloq. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_keeloq_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderKeeloq. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + * @param output Resulting text + */ +void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/keeloq_common.c b/applications/main/subghz/protocols/keeloq_common.c new file mode 100644 index 000000000..1d2d04457 --- /dev/null +++ b/applications/main/subghz/protocols/keeloq_common.c @@ -0,0 +1,142 @@ +#include "keeloq_common.h" + +#include + +#include + +#define bit(x, n) (((x) >> (n)) & 1) +#define g5(x, a, b, c, d, e) \ + (bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16) + +/** Simple Learning Encrypt + * @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter + * @param key - manufacture (64bit) + * @return keeloq encrypt data + */ +inline uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key) { + uint32_t x = data, r; + for(r = 0; r < 528; r++) + x = (x >> 1) ^ ((bit(x, 0) ^ bit(x, 16) ^ (uint32_t)bit(key, r & 63) ^ + bit(KEELOQ_NLF, g5(x, 1, 9, 20, 26, 31))) + << 31); + return x; +} + +/** Simple Learning Decrypt + * @param data - keelog encrypt data + * @param key - manufacture (64bit) + * @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter + */ +inline uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key) { + uint32_t x = data, r; + for(r = 0; r < 528; r++) + x = (x << 1) ^ bit(x, 31) ^ bit(x, 15) ^ (uint32_t)bit(key, (15 - r) & 63) ^ + bit(KEELOQ_NLF, g5(x, 0, 8, 19, 25, 30)); + return x; +} + +/** Normal Learning + * @param data - serial number (28bit) + * @param key - manufacture (64bit) + * @return manufacture for this serial number (64bit) + */ +inline uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key) { + uint32_t k1, k2; + + data &= 0x0FFFFFFF; + data |= 0x20000000; + k1 = subghz_protocol_keeloq_common_decrypt(data, key); + + data &= 0x0FFFFFFF; + data |= 0x60000000; + k2 = subghz_protocol_keeloq_common_decrypt(data, key); + + return ((uint64_t)k2 << 32) | k1; // key - shifrovanoya +} + +/** Secure Learning + * @param data - serial number (28bit) + * @param seed - seed number (32bit) + * @param key - manufacture (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t subghz_protocol_keeloq_common_secure_learning( + uint32_t data, + uint32_t seed, + const uint64_t key) { + uint32_t k1, k2; + + data &= 0x0FFFFFFF; + k1 = subghz_protocol_keeloq_common_decrypt(data, key); + k2 = subghz_protocol_keeloq_common_decrypt(seed, key); + + return ((uint64_t)k1 << 32) | k2; +} + +/** Magic_xor_type1 Learning + * @param data - serial number (28bit) + * @param xor - magic xor (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor) { + data &= 0x0FFFFFFF; + return (((uint64_t)data << 32) | data) ^ xor; +} + +/** Faac SLH (Spa) Learning + * @param seed - seed number (32bit) + * @param key - mfkey (64bit) + * @return man_learning for this seed number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_faac_learning(const uint32_t seed, const uint64_t key) { + uint16_t hs = seed >> 16; + const uint16_t ending = 0x544D; + uint32_t lsb = (uint32_t)hs << 16 | ending; + uint64_t man = (uint64_t)subghz_protocol_keeloq_common_encrypt(seed, key) << 32 | + subghz_protocol_keeloq_common_encrypt(lsb, key); + return man; +} +/** Magic_serial_type1 Learning + * @param data - serial number (28bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) { + return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) | + ((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32); +} + +/** Magic_serial_type2 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) { + uint8_t* p = (uint8_t*)&data; + uint8_t* m = (uint8_t*)&man; + m[7] = p[0]; + m[6] = p[1]; + m[5] = p[2]; + m[4] = p[3]; + return man; +} + +/** Magic_serial_type3 Learning + * @param data - serial number (24bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) { + return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF); +} diff --git a/applications/main/subghz/protocols/keeloq_common.h b/applications/main/subghz/protocols/keeloq_common.h new file mode 100644 index 000000000..a6c0d346e --- /dev/null +++ b/applications/main/subghz/protocols/keeloq_common.h @@ -0,0 +1,100 @@ +#pragma once + +#include "base.h" + +#include + +/* + * Keeloq + * https://ru.wikipedia.org/wiki/KeeLoq + * https://phreakerclub.com/forum/showthread.php?t=1094 + * + */ +#define KEELOQ_NLF 0x3A5C742E + +/* + * KeeLoq learning types + * https://phreakerclub.com/forum/showthread.php?t=67 + */ +#define KEELOQ_LEARNING_UNKNOWN 0u +#define KEELOQ_LEARNING_SIMPLE 1u +#define KEELOQ_LEARNING_NORMAL 2u +#define KEELOQ_LEARNING_SECURE 3u +#define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1 4u +#define KEELOQ_LEARNING_FAAC 5u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 6u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 7u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 8u + +/** + * Simple Learning Encrypt + * @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter + * @param key - manufacture (64bit) + * @return keeloq encrypt data + */ +uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key); + +/** + * Simple Learning Decrypt + * @param data - keeloq encrypt data + * @param key - manufacture (64bit) + * @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter + */ +uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key); + +/** + * Normal Learning + * @param data - serial number (28bit) + * @param key - manufacture (64bit) + * @return manufacture for this serial number (64bit) + */ +uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key); + +/** + * Secure Learning + * @param data - serial number (28bit) + * @param seed - seed number (32bit) + * @param key - manufacture (64bit) + * @return manufacture for this serial number (64bit) + */ +uint64_t + subghz_protocol_keeloq_common_secure_learning(uint32_t data, uint32_t seed, const uint64_t key); + +/** + * Magic_xor_type1 Learning + * @param data - serial number (28bit) + * @param xor - magic xor (64bit) + * @return manufacture for this serial number (64bit) + */ +uint64_t subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor); + +/** Faac SLH (Spa) Learning + * @param seed - seed number (32bit) + * @param key - mfkey (64bit) + * @return man_learning for this fix number (64bit) + */ +uint64_t subghz_protocol_keeloq_common_faac_learning(const uint32_t seed, const uint64_t key); + +/** Magic_serial_type1 Learning + * @param data - serial number (28bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man); + +/** Magic_serial_type2 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man); + +/** Magic_serial_type3 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man); diff --git a/applications/main/subghz/protocols/kia.c b/applications/main/subghz/protocols/kia.c new file mode 100644 index 000000000..a5d9e37ef --- /dev/null +++ b/applications/main/subghz/protocols/kia.c @@ -0,0 +1,279 @@ +#include "kia.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoKIA" + +static const SubGhzBlockConst subghz_protocol_kia_const = { + .te_short = 250, + .te_long = 500, + .te_delta = 100, + .min_count_bit_for_found = 61, +}; + +struct SubGhzProtocolDecoderKIA { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderKIA { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + KIADecoderStepReset = 0, + KIADecoderStepCheckPreambula, + KIADecoderStepSaveDuration, + KIADecoderStepCheckDuration, +} KIADecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_kia_decoder = { + .alloc = subghz_protocol_decoder_kia_alloc, + .free = subghz_protocol_decoder_kia_free, + + .feed = subghz_protocol_decoder_kia_feed, + .reset = subghz_protocol_decoder_kia_reset, + + .get_hash_data = subghz_protocol_decoder_kia_get_hash_data, + .serialize = subghz_protocol_decoder_kia_serialize, + .deserialize = subghz_protocol_decoder_kia_deserialize, + .get_string = subghz_protocol_decoder_kia_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_kia_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_kia = { + .name = SUBGHZ_PROTOCOL_KIA_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_kia_decoder, + .encoder = &subghz_protocol_kia_encoder, +}; + +void* subghz_protocol_decoder_kia_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderKIA* instance = malloc(sizeof(SubGhzProtocolDecoderKIA)); + instance->base.protocol = &subghz_protocol_kia; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_kia_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + free(instance); +} + +void subghz_protocol_decoder_kia_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + instance->decoder.parser_step = KIADecoderStepReset; +} + +void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + + switch(instance->decoder.parser_step) { + case KIADecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta)) { + instance->decoder.parser_step = KIADecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + case KIADecoderStepCheckPreambula: + if(level) { + if((DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) < + subghz_protocol_kia_const.te_delta)) { + instance->decoder.te_last = duration; + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + } else if( + (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta) && + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta)) { + // Found header + instance->header_count++; + break; + } else if( + (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) < + subghz_protocol_kia_const.te_delta) && + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_long) < + subghz_protocol_kia_const.te_delta)) { + // Found start bit + if(instance->header_count > 15) { + instance->decoder.parser_step = KIADecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 1; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + break; + case KIADecoderStepSaveDuration: + if(level) { + if(duration >= + (subghz_protocol_kia_const.te_long + subghz_protocol_kia_const.te_delta * 2UL)) { + //Found stop bit + instance->decoder.parser_step = KIADecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_kia_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KIADecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + break; + case KIADecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = KIADecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_long) < + subghz_protocol_kia_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) < + subghz_protocol_kia_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = KIADecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_kia_crc8(uint8_t* data, size_t len) { + uint8_t crc = 0x08; + size_t i, j; + for(i = 0; i < len; i++) { + crc ^= data[i]; + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x7F); + else + crc <<= 1; + } + } + return crc; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_kia_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * 0x0F 0112 43B04EC 1 7D + * 0x0F 0113 43B04EC 1 DF + * 0x0F 0114 43B04EC 1 30 + * 0x0F 0115 43B04EC 2 13 + * 0x0F 0116 43B04EC 3 F5 + * CNT Serial K CRC8 Kia (CRC8, poly 0x7f, start_crc 0x08) + */ + + instance->serial = (uint32_t)((instance->data >> 12) & 0x0FFFFFFF); + instance->btn = (instance->data >> 8) & 0x0F; + instance->cnt = (instance->data >> 40) & 0xFFFF; +} + +uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_kia_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != subghz_protocol_kia_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + + subghz_protocol_kia_check_remote_controller(&instance->generic); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%08lX%08lX\r\n" + "Sn:%07lX Btn:%X Cnt:%04lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/applications/main/subghz/protocols/kia.h b/applications/main/subghz/protocols/kia.h new file mode 100644 index 000000000..a9afcf149 --- /dev/null +++ b/applications/main/subghz/protocols/kia.h @@ -0,0 +1,73 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_KIA_NAME "KIA Seed" + +typedef struct SubGhzProtocolDecoderKIA SubGhzProtocolDecoderKIA; +typedef struct SubGhzProtocolEncoderKIA SubGhzProtocolEncoderKIA; + +extern const SubGhzProtocolDecoder subghz_protocol_kia_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_kia_encoder; +extern const SubGhzProtocol subghz_protocol_kia; + +/** + * Allocate SubGhzProtocolDecoderKIA. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderKIA* pointer to a SubGhzProtocolDecoderKIA instance + */ +void* subghz_protocol_decoder_kia_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderKIA. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + */ +void subghz_protocol_decoder_kia_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderKIA. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + */ +void subghz_protocol_decoder_kia_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderKIA. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_kia_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderKIA. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + * @param output Resulting text + */ +void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/kinggates_stylo_4k.c b/applications/main/subghz/protocols/kinggates_stylo_4k.c new file mode 100644 index 000000000..5f2a83d77 --- /dev/null +++ b/applications/main/subghz/protocols/kinggates_stylo_4k.c @@ -0,0 +1,581 @@ +#include "kinggates_stylo_4k.h" +#include "keeloq_common.h" + +#include "../subghz_keystore.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoKingGates_stylo_4k" + +static const SubGhzBlockConst subghz_protocol_kinggates_stylo_4k_const = { + .te_short = 400, + .te_long = 1100, + .te_delta = 140, + .min_count_bit_for_found = 89, +}; + +struct SubGhzProtocolDecoderKingGates_stylo_4k { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + SubGhzKeystore* keystore; +}; + +struct SubGhzProtocolEncoderKingGates_stylo_4k { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + SubGhzKeystore* keystore; +}; + +typedef enum { + KingGates_stylo_4kDecoderStepReset = 0, + KingGates_stylo_4kDecoderStepCheckPreambula, + KingGates_stylo_4kDecoderStepCheckStartBit, + KingGates_stylo_4kDecoderStepSaveDuration, + KingGates_stylo_4kDecoderStepCheckDuration, +} KingGates_stylo_4kDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = { + .alloc = subghz_protocol_decoder_kinggates_stylo_4k_alloc, + .free = subghz_protocol_decoder_kinggates_stylo_4k_free, + + .feed = subghz_protocol_decoder_kinggates_stylo_4k_feed, + .reset = subghz_protocol_decoder_kinggates_stylo_4k_reset, + + .get_hash_data = subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data, + .serialize = subghz_protocol_decoder_kinggates_stylo_4k_serialize, + .deserialize = subghz_protocol_decoder_kinggates_stylo_4k_deserialize, + .get_string = subghz_protocol_decoder_kinggates_stylo_4k_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = { + .alloc = subghz_protocol_encoder_kinggates_stylo_4k_alloc, + .free = subghz_protocol_encoder_kinggates_stylo_4k_free, + + .deserialize = subghz_protocol_encoder_kinggates_stylo_4k_deserialize, + .stop = subghz_protocol_encoder_kinggates_stylo_4k_stop, + .yield = subghz_protocol_encoder_kinggates_stylo_4k_yield, +}; + +const SubGhzProtocol subghz_protocol_kinggates_stylo_4k = { + .name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_kinggates_stylo_4k_decoder, + .encoder = &subghz_protocol_kinggates_stylo_4k_encoder, +}; + +// +// Encoder +// + +// Pre define function +static void subghz_protocol_kinggates_stylo_4k_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore); + +void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderKingGates_stylo_4k* instance = + malloc(sizeof(SubGhzProtocolEncoderKingGates_stylo_4k)); + + instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + free(instance->encoder.upload); + free(instance); +} + +void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context) { + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context) { + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +/** + * Key generation from simple data + * @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k* instance + * @param btn Button number, 4 bit + */ +static bool subghz_protocol_kinggates_stylo_4k_gen_data( + SubGhzProtocolEncoderKingGates_stylo_4k* instance, + uint8_t btn) { + UNUSED(btn); + uint32_t hop = subghz_protocol_blocks_reverse_key(instance->generic.data_2 >> 4, 32); + uint64_t fix = subghz_protocol_blocks_reverse_key(instance->generic.data, 53); + int res = 0; + uint32_t decrypt = 0; + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k"); + if(res == 0) { + //Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + break; + } + } + instance->generic.cnt = decrypt & 0xFFFF; + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + instance->generic.btn = (fix >> 17) & 0x0F; + instance->generic.serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); + + uint32_t data = (decrypt & 0xFFFF0000) | instance->generic.cnt; + + uint64_t encrypt = 0; + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k"); + if(res == 0) { + //Simple Learning + encrypt = subghz_protocol_keeloq_common_encrypt(data, manufacture_code->key); + encrypt = subghz_protocol_blocks_reverse_key(encrypt, 32); + instance->generic.data_2 = encrypt << 4; + return true; + } + } + + return false; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @return true On success + */ +static bool subghz_protocol_encoder_kinggates_stylo_4k_get_upload( + SubGhzProtocolEncoderKingGates_stylo_4k* instance, + uint8_t btn) { + furi_assert(instance); + + // Gen new key + if(subghz_protocol_kinggates_stylo_4k_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + // Start + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)9500); + + // Send header + for(uint8_t i = 12; i > 0; i--) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + + // After header + instance->encoder.upload[index - 1].duration = + (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 2; + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short * 2); + + // Send key fix + for(uint8_t i = 53; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + } + + // Send key hop + for(uint8_t i = 36; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + } + + // Set upload size after generating upload, fix it later + + instance->encoder.size_upload = index; + + return true; +} + +bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + subghz_protocol_kinggates_stylo_4k_remote_controller( + &instance->generic, instance->keystore); + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i]; + } + + subghz_protocol_encoder_kinggates_stylo_4k_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +// +// Decoder +// +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderKingGates_stylo_4k* instance = + malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k)); + instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + return instance; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + free(instance); +} + +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + + switch(instance->decoder.parser_step) { + case KingGates_stylo_4kDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case KingGates_stylo_4kDecoderStepCheckPreambula: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + // Found header + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckStartBit; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckStartBit: + if((level) && + DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->generic.data_2 = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + instance->generic.data = instance->generic.data_2; + instance->generic.data_2 = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->decoder.decode_data = 0; + instance->generic.data_2 = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + if(instance->decoder.decode_count_bit == 53) { + instance->generic.data_2 = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param data Input encrypted data + * @param keystore Pointer to a SubGhzKeystore* instance + */ +static void subghz_protocol_kinggates_stylo_4k_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore) { + /** + * 9500us 12*(400/400) 2200/800|1-bit|0-bit| + * _ _ _ __ ___ _ + * ________| |_| |_..._| |_____| |_| |___| |..... + * + * 1-bit 400/1100 us + * 0-bit 1100/400 us + * + * The package consists of 89 bits of data, LSB first + * Data - 1C9037F0C80000 CE280BA00 + * S[3] S[2] 1 key S[1] S[0] 2 byte always 0 Hop[3] Hop[2] Hop[1] Hop[0] 0 + * 11100100 10000001 1 0111 11110000 11001000 00000000 00000000 11001110 00101000 00001011 10100000 0000 + * + * Encryption - keeloq Simple Learning + * key C S[3] CNT + * Decrypt - 0xEC270B9C => 0x E C 27 0B9C + * + * + * +*/ + + uint32_t hop = subghz_protocol_blocks_reverse_key(instance->data_2 >> 4, 32); + uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53); + bool ret = false; + uint32_t decrypt = 0; + instance->btn = (fix >> 17) & 0x0F; + instance->serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE) { + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(((decrypt >> 28) == instance->btn) && (((decrypt >> 24) & 0x0F) == 0x0C) && + (((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF))) { + ret = true; + break; + } + } + } + if(ret) { + instance->cnt = decrypt & 0xFFFF; + } else { + instance->btn = 0; + instance->serial = 0; + instance->cnt = 0; + } +} + +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> (i * 8)) & 0xFF; + } + + if(res && !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i]; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + subghz_protocol_kinggates_stylo_4k_remote_controller(&instance->generic, instance->keystore); + + furi_string_cat_printf( + output, + "%s\r\n" + "Key:0x%llX%07llX %dbit\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%04lX\r\n", + instance->generic.protocol_name, + instance->generic.data, + instance->generic.data_2, + instance->generic.data_count_bit, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} \ No newline at end of file diff --git a/applications/main/subghz/protocols/kinggates_stylo_4k.h b/applications/main/subghz/protocols/kinggates_stylo_4k.h new file mode 100644 index 000000000..9717f6715 --- /dev/null +++ b/applications/main/subghz/protocols/kinggates_stylo_4k.h @@ -0,0 +1,110 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME "KingGates Stylo4k" + +typedef struct SubGhzProtocolDecoderKingGates_stylo_4k SubGhzProtocolDecoderKingGates_stylo_4k; +typedef struct SubGhzProtocolEncoderKingGates_stylo_4k SubGhzProtocolEncoderKingGates_stylo_4k; + +extern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder; +extern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k; + +/** + * Allocate SubGhzProtocolEncoderKingGates_stylo_4k. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderKingGates_stylo_4k* pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderKingGates_stylo_4k. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderKingGates_stylo_4k* pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param output Resulting text + */ +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/linear.c b/applications/main/subghz/protocols/linear.c new file mode 100644 index 000000000..2fc8b20c8 --- /dev/null +++ b/applications/main/subghz/protocols/linear.c @@ -0,0 +1,352 @@ +#include "linear.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolLinear" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define DATA_TO_DIP(dip) \ + (dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \ + (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \ + (dip & 0x0001 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_linear_const = { + .te_short = 500, + .te_long = 1500, + .te_delta = 150, + .min_count_bit_for_found = 10, +}; + +struct SubGhzProtocolDecoderLinear { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderLinear { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + LinearDecoderStepReset = 0, + LinearDecoderStepSaveDuration, + LinearDecoderStepCheckDuration, +} LinearDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_linear_decoder = { + .alloc = subghz_protocol_decoder_linear_alloc, + .free = subghz_protocol_decoder_linear_free, + + .feed = subghz_protocol_decoder_linear_feed, + .reset = subghz_protocol_decoder_linear_reset, + + .get_hash_data = subghz_protocol_decoder_linear_get_hash_data, + .serialize = subghz_protocol_decoder_linear_serialize, + .deserialize = subghz_protocol_decoder_linear_deserialize, + .get_string = subghz_protocol_decoder_linear_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_linear_encoder = { + .alloc = subghz_protocol_encoder_linear_alloc, + .free = subghz_protocol_encoder_linear_free, + + .deserialize = subghz_protocol_encoder_linear_deserialize, + .stop = subghz_protocol_encoder_linear_stop, + .yield = subghz_protocol_encoder_linear_yield, +}; + +const SubGhzProtocol subghz_protocol_linear = { + .name = SUBGHZ_PROTOCOL_LINEAR_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_linear_decoder, + .encoder = &subghz_protocol_linear_encoder, +}; + +void* subghz_protocol_encoder_linear_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderLinear* instance = malloc(sizeof(SubGhzProtocolEncoderLinear)); + + instance->base.protocol = &subghz_protocol_linear; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 28; //max 10bit*2 + 2 (start, stop) + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_linear_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderLinear* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderLinear instance + * @return true On success + */ +static bool subghz_protocol_encoder_linear_get_upload(SubGhzProtocolEncoderLinear* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 3); + } + } + //Send end bit + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short * 3); + //Send PT_GUARD + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 42); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short); + //Send PT_GUARD + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 44); + } + + return true; +} + +bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderLinear* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_linear_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_linear_stop(void* context) { + SubGhzProtocolEncoderLinear* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_linear_yield(void* context) { + SubGhzProtocolEncoderLinear* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_linear_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderLinear* instance = malloc(sizeof(SubGhzProtocolDecoderLinear)); + instance->base.protocol = &subghz_protocol_linear; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_linear_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + free(instance); +} + +void subghz_protocol_decoder_linear_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + instance->decoder.parser_step = LinearDecoderStepReset; +} + +void subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + switch(instance->decoder.parser_step) { + case LinearDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_linear_const.te_short * 42) < + subghz_protocol_linear_const.te_delta * 20)) { + //Found header Linear + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } + break; + case LinearDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LinearDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + case LinearDecoderStepCheckDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_linear_const.te_short * 5)) { + instance->decoder.parser_step = LinearDecoderStepReset; + //checking that the duration matches the guardtime + if((DURATION_DIFF(duration, subghz_protocol_linear_const.te_short * 42) > + subghz_protocol_linear_const.te_delta * 20)) { + break; + } + if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_short) < + subghz_protocol_linear_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_long) < + subghz_protocol_linear_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } + if(instance->decoder.decode_count_bit == + subghz_protocol_linear_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_short) < + subghz_protocol_linear_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_const.te_long) < + subghz_protocol_linear_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_long) < + subghz_protocol_linear_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_const.te_short) < + subghz_protocol_linear_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_linear_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_linear_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Yek:0x%08lX\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_lo, + code_found_reverse_lo, + DATA_TO_DIP(code_found_lo)); +} diff --git a/applications/main/subghz/protocols/linear.h b/applications/main/subghz/protocols/linear.h new file mode 100644 index 000000000..923337ac2 --- /dev/null +++ b/applications/main/subghz/protocols/linear.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_LINEAR_NAME "Linear" + +typedef struct SubGhzProtocolDecoderLinear SubGhzProtocolDecoderLinear; +typedef struct SubGhzProtocolEncoderLinear SubGhzProtocolEncoderLinear; + +extern const SubGhzProtocolDecoder subghz_protocol_linear_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_linear_encoder; +extern const SubGhzProtocol subghz_protocol_linear; + +/** + * Allocate SubGhzProtocolEncoderLinear. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderLinear* pointer to a SubGhzProtocolEncoderLinear instance + */ +void* subghz_protocol_encoder_linear_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderLinear. + * @param context Pointer to a SubGhzProtocolEncoderLinear instance + */ +void subghz_protocol_encoder_linear_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderLinear instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderLinear instance + */ +void subghz_protocol_encoder_linear_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderLinear instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_linear_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderLinear. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderLinear* pointer to a SubGhzProtocolDecoderLinear instance + */ +void* subghz_protocol_decoder_linear_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderLinear. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + */ +void subghz_protocol_decoder_linear_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderLinear. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + */ +void subghz_protocol_decoder_linear_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderLinear. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_linear_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderLinear. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + * @param output Resulting text + */ +void subghz_protocol_decoder_linear_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/linear_delta3.c b/applications/main/subghz/protocols/linear_delta3.c new file mode 100644 index 000000000..869edac84 --- /dev/null +++ b/applications/main/subghz/protocols/linear_delta3.c @@ -0,0 +1,359 @@ +#include "linear_delta3.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolLinearDelta3" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define DATA_TO_DIP(dip) \ + (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \ + (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0'), (dip & 0x0001 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_linear_delta3_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 8, +}; + +struct SubGhzProtocolDecoderLinearDelta3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderLinearDelta3 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + LinearDecoderStepReset = 0, + LinearDecoderStepSaveDuration, + LinearDecoderStepCheckDuration, +} LinearDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder = { + .alloc = subghz_protocol_decoder_linear_delta3_alloc, + .free = subghz_protocol_decoder_linear_delta3_free, + + .feed = subghz_protocol_decoder_linear_delta3_feed, + .reset = subghz_protocol_decoder_linear_delta3_reset, + + .get_hash_data = subghz_protocol_decoder_linear_delta3_get_hash_data, + .serialize = subghz_protocol_decoder_linear_delta3_serialize, + .deserialize = subghz_protocol_decoder_linear_delta3_deserialize, + .get_string = subghz_protocol_decoder_linear_delta3_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder = { + .alloc = subghz_protocol_encoder_linear_delta3_alloc, + .free = subghz_protocol_encoder_linear_delta3_free, + + .deserialize = subghz_protocol_encoder_linear_delta3_deserialize, + .stop = subghz_protocol_encoder_linear_delta3_stop, + .yield = subghz_protocol_encoder_linear_delta3_yield, +}; + +const SubGhzProtocol subghz_protocol_linear_delta3 = { + .name = SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_linear_delta3_decoder, + .encoder = &subghz_protocol_linear_delta3_encoder, +}; + +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolEncoderLinearDelta3)); + + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 16; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_linear_delta3_get_upload(SubGhzProtocolEncoderLinearDelta3* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + } + } + //Send end bit + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 73); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 70); + } + + return true; +} + +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_linear_delta3_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_linear_delta3_stop(void* context) { + SubGhzProtocolEncoderLinearDelta3* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context) { + SubGhzProtocolEncoderLinearDelta3* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolDecoderLinearDelta3)); + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + free(instance); +} + +void subghz_protocol_decoder_linear_delta3_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + instance->decoder.parser_step = LinearDecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + switch(instance->decoder.parser_step) { + case LinearDecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 70) < + subghz_protocol_linear_delta3_const.te_delta * 24)) { + //Found header Linear + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } + break; + case LinearDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LinearDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + case LinearDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_linear_delta3_const.te_short * 10)) { + instance->decoder.parser_step = LinearDecoderStepReset; + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + if(instance->decoder.decode_count_bit == + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + instance->last_data = instance->decoder.decode_data; + } + break; + } + + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 7) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8)); +} + +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + + uint32_t data = instance->generic.data & 0xFF; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + data, + DATA_TO_DIP(data)); +} diff --git a/applications/main/subghz/protocols/linear_delta3.h b/applications/main/subghz/protocols/linear_delta3.h new file mode 100644 index 000000000..2f0a32e68 --- /dev/null +++ b/applications/main/subghz/protocols/linear_delta3.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME "LinearDelta3" + +typedef struct SubGhzProtocolDecoderLinearDelta3 SubGhzProtocolDecoderLinearDelta3; +typedef struct SubGhzProtocolEncoderLinearDelta3 SubGhzProtocolEncoderLinearDelta3; + +extern const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder; +extern const SubGhzProtocol subghz_protocol_linear_delta3; + +/** + * Allocate SubGhzProtocolEncoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderLinearDelta3* pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderLinearDelta3* pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/magellan.c b/applications/main/subghz/protocols/magellan.c new file mode 100644 index 000000000..67d3fe3d3 --- /dev/null +++ b/applications/main/subghz/protocols/magellan.c @@ -0,0 +1,445 @@ +#include "magellan.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolMagellan" + +static const SubGhzBlockConst subghz_protocol_magellan_const = { + .te_short = 200, + .te_long = 400, + .te_delta = 100, + .min_count_bit_for_found = 32, +}; + +struct SubGhzProtocolDecoderMagellan { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderMagellan { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + MagellanDecoderStepReset = 0, + MagellanDecoderStepCheckPreambula, + MagellanDecoderStepFoundPreambula, + MagellanDecoderStepSaveDuration, + MagellanDecoderStepCheckDuration, +} MagellanDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_magellan_decoder = { + .alloc = subghz_protocol_decoder_magellan_alloc, + .free = subghz_protocol_decoder_magellan_free, + + .feed = subghz_protocol_decoder_magellan_feed, + .reset = subghz_protocol_decoder_magellan_reset, + + .get_hash_data = subghz_protocol_decoder_magellan_get_hash_data, + .serialize = subghz_protocol_decoder_magellan_serialize, + .deserialize = subghz_protocol_decoder_magellan_deserialize, + .get_string = subghz_protocol_decoder_magellan_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_magellan_encoder = { + .alloc = subghz_protocol_encoder_magellan_alloc, + .free = subghz_protocol_encoder_magellan_free, + + .deserialize = subghz_protocol_encoder_magellan_deserialize, + .stop = subghz_protocol_encoder_magellan_stop, + .yield = subghz_protocol_encoder_magellan_yield, +}; + +const SubGhzProtocol subghz_protocol_magellan = { + .name = SUBGHZ_PROTOCOL_MAGELLAN_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_magellan_decoder, + .encoder = &subghz_protocol_magellan_encoder, +}; + +void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMagellan* instance = malloc(sizeof(SubGhzProtocolEncoderMagellan)); + + instance->base.protocol = &subghz_protocol_magellan; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_magellan_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMagellan* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMagellan instance + * @return true On success + */ +static bool subghz_protocol_encoder_magellan_get_upload(SubGhzProtocolEncoderMagellan* instance) { + furi_assert(instance); + + size_t index = 0; + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short * 4); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + for(uint8_t i = 0; i < 12; i++) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + } + } + + //Send stop bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long * 100); + + instance->encoder.size_upload = index; + return true; +} + +bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMagellan* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_magellan_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_magellan_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_magellan_stop(void* context) { + SubGhzProtocolEncoderMagellan* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_magellan_yield(void* context) { + SubGhzProtocolEncoderMagellan* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMagellan* instance = malloc(sizeof(SubGhzProtocolDecoderMagellan)); + instance->base.protocol = &subghz_protocol_magellan; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_magellan_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + free(instance); +} + +void subghz_protocol_decoder_magellan_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + instance->decoder.parser_step = MagellanDecoderStepReset; +} + +uint8_t subghz_protocol_magellan_crc8(uint8_t* data, size_t len) { + uint8_t crc = 0x00; + size_t i, j; + for(i = 0; i < len; i++) { + crc ^= data[i]; + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; +} + +static bool subghz_protocol_magellan_check_crc(SubGhzProtocolDecoderMagellan* instance) { + uint8_t data[3] = { + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8}; + return (instance->decoder.decode_data & 0xFF) == + subghz_protocol_magellan_crc8(data, sizeof(data)); +} + +void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + + switch(instance->decoder.parser_step) { + case MagellanDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + instance->decoder.parser_step = MagellanDecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case MagellanDecoderStepCheckPreambula: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + // Found header + instance->header_count++; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta * 2) && + (instance->header_count > 10)) { + instance->decoder.parser_step = MagellanDecoderStepFoundPreambula; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } + break; + + case MagellanDecoderStepFoundPreambula: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_magellan_const.te_short * 6) < + subghz_protocol_magellan_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta * 2)) { + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } + break; + + case MagellanDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = MagellanDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + break; + + case MagellanDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + } else if(duration >= (subghz_protocol_magellan_const.te_long * 3)) { + //Found stop bit + if((instance->decoder.decode_count_bit == + subghz_protocol_magellan_const.min_count_bit_for_found) && + subghz_protocol_magellan_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = MagellanDecoderStepReset; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_magellan_check_remote_controller(SubGhzBlockGeneric* instance) { + /* +* package 32b data 24b CRC8 +* 0x037AE4828 => 001101111010111001001000 00101000 +* +* 0x037AE48 (flipped in reverse bit sequence) => 0x1275EC +* +* 0x1275EC => 0x12-event codes, 0x75EC-serial (dec 117236) +* +* event codes +* bit_0: 1-Open/Motion, 0-close/ok +* bit_1: 1-Tamper On (alarm), 0-Tamper Off (ok) +* bit_2: ? +* bit_3: 1-power on +* bit_4: model type - wireless reed +* bit_5: model type - motion sensor +* bit_6: ? +* bit_7: ? +* +*/ + uint64_t data_rev = subghz_protocol_blocks_reverse_key(instance->data >> 8, 24); + instance->serial = data_rev & 0xFFFF; + instance->btn = (data_rev >> 16) & 0xFF; +} + +static void subghz_protocol_magellan_get_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s%s%s%s%s", + ((event >> 4) & 0x1 ? (event & 0x1 ? " Open" : " Close") : + (event & 0x1 ? " Motion" : " Ok")), + ((event >> 1) & 0x1 ? ", Tamper On\n(Alarm)" : ""), + ((event >> 2) & 0x1 ? ", ?" : ""), + ((event >> 3) & 0x1 ? ", Power On" : ""), + ((event >> 4) & 0x1 ? ", MT:Wireless_Reed" : ""), + ((event >> 5) & 0x1 ? ", MT:Motion_\nSensor" : ""), + ((event >> 6) & 0x1 ? ", ?" : ""), + ((event >> 7) & 0x1 ? ", ?" : "")); +} + +uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_magellan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_magellan_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + subghz_protocol_magellan_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Sn:%03ld%03ld, Event:0x%02X\r\n" + "Stat:", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + (instance->generic.serial >> 8) & 0xFF, + instance->generic.serial & 0xFF, + instance->generic.btn); + + subghz_protocol_magellan_get_event_serialize(instance->generic.btn, output); +} diff --git a/applications/main/subghz/protocols/magellan.h b/applications/main/subghz/protocols/magellan.h new file mode 100644 index 000000000..a179c9cb4 --- /dev/null +++ b/applications/main/subghz/protocols/magellan.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MAGELLAN_NAME "Magellan" + +typedef struct SubGhzProtocolDecoderMagellan SubGhzProtocolDecoderMagellan; +typedef struct SubGhzProtocolEncoderMagellan SubGhzProtocolEncoderMagellan; + +extern const SubGhzProtocolDecoder subghz_protocol_magellan_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_magellan_encoder; +extern const SubGhzProtocol subghz_protocol_magellan; + +/** + * Allocate SubGhzProtocolEncoderMagellan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMagellan* pointer to a SubGhzProtocolEncoderMagellan instance + */ +void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMagellan. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + */ +void subghz_protocol_encoder_magellan_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + */ +void subghz_protocol_encoder_magellan_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_magellan_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMagellan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMagellan* pointer to a SubGhzProtocolDecoderMagellan instance + */ +void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + */ +void subghz_protocol_decoder_magellan_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + */ +void subghz_protocol_decoder_magellan_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_magellan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param output Resulting text + */ +void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/marantec.c b/applications/main/subghz/protocols/marantec.c new file mode 100644 index 000000000..d557c29b0 --- /dev/null +++ b/applications/main/subghz/protocols/marantec.c @@ -0,0 +1,393 @@ +#include "marantec.h" +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolMarantec" + +static const SubGhzBlockConst subghz_protocol_marantec_const = { + .te_short = 1000, + .te_long = 2000, + .te_delta = 200, + .min_count_bit_for_found = 49, +}; + +struct SubGhzProtocolDecoderMarantec { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + ManchesterState manchester_saved_state; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderMarantec { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + MarantecDecoderStepReset = 0, + MarantecDecoderFoundHeader, + MarantecDecoderStepDecoderData, +} MarantecDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_marantec_decoder = { + .alloc = subghz_protocol_decoder_marantec_alloc, + .free = subghz_protocol_decoder_marantec_free, + + .feed = subghz_protocol_decoder_marantec_feed, + .reset = subghz_protocol_decoder_marantec_reset, + + .get_hash_data = subghz_protocol_decoder_marantec_get_hash_data, + .serialize = subghz_protocol_decoder_marantec_serialize, + .deserialize = subghz_protocol_decoder_marantec_deserialize, + .get_string = subghz_protocol_decoder_marantec_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_marantec_encoder = { + .alloc = subghz_protocol_encoder_marantec_alloc, + .free = subghz_protocol_encoder_marantec_free, + + .deserialize = subghz_protocol_encoder_marantec_deserialize, + .stop = subghz_protocol_encoder_marantec_stop, + .yield = subghz_protocol_encoder_marantec_yield, +}; + +const SubGhzProtocol subghz_protocol_marantec = { + .name = SUBGHZ_PROTOCOL_MARANTEC_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_marantec_decoder, + .encoder = &subghz_protocol_marantec_encoder, +}; + +void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMarantec* instance = malloc(sizeof(SubGhzProtocolEncoderMarantec)); + + instance->base.protocol = &subghz_protocol_marantec; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_marantec_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMarantec* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static LevelDuration + subghz_protocol_encoder_marantec_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_marantec_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_marantec_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_marantec_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_marantec_const.te_short; + data.level = true; + break; + + default: + furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMarantec instance + */ +static void subghz_protocol_encoder_marantec_get_upload(SubGhzProtocolEncoderMarantec* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + if(!manchester_encoder_advance( + &enc_state, + bit_read(instance->generic.data, instance->generic.data_count_bit - 1), + &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_marantec_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, + bit_read(instance->generic.data, instance->generic.data_count_bit - 1), + &result); + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_marantec_const.te_long * 5); + + for(uint8_t i = instance->generic.data_count_bit - 1; i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_marantec_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_marantec_add_duration_to_upload(result); + } + instance->encoder.upload[index] = subghz_protocol_encoder_marantec_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + instance->encoder.size_upload = index; +} + +uint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len) { + uint8_t crc = 0x08; + size_t i, j; + for(i = 0; i < len; i++) { + crc ^= data[i]; + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x1D); + else + crc <<= 1; + } + } + return crc; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_marantec_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = (instance->data >> 16) & 0xF; + instance->serial = ((instance->data >> 12) & 0xFFFFFF00) | ((instance->data >> 8) & 0xFF); +} + +bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMarantec* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_marantec_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_marantec_remote_controller(&instance->generic); + subghz_protocol_encoder_marantec_get_upload(instance); + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_marantec_stop(void* context) { + SubGhzProtocolEncoderMarantec* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_marantec_yield(void* context) { + SubGhzProtocolEncoderMarantec* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_marantec_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMarantec* instance = malloc(sizeof(SubGhzProtocolDecoderMarantec)); + instance->base.protocol = &subghz_protocol_marantec; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_marantec_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + free(instance); +} + +void subghz_protocol_decoder_marantec_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +void subghz_protocol_decoder_marantec_feed(void* context, bool level, volatile uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + ManchesterEvent event = ManchesterEventReset; + + switch(instance->decoder.parser_step) { + case MarantecDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long * 5) < + subghz_protocol_marantec_const.te_delta * 8)) { + //Found header marantec + instance->decoder.parser_step = MarantecDecoderStepDecoderData; + instance->decoder.decode_data = 1; + instance->decoder.decode_count_bit = 1; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + } + break; + case MarantecDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= ((uint32_t)subghz_protocol_marantec_const.te_long * 2 + + subghz_protocol_marantec_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_marantec_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 1; + instance->decoder.decode_count_bit = 1; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + } else { + instance->decoder.parser_step = MarantecDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = MarantecDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_marantec_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_marantec_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + subghz_protocol_marantec_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%07lX \r\n" + "Btn:%X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/marantec.h b/applications/main/subghz/protocols/marantec.h new file mode 100644 index 000000000..e330ccf16 --- /dev/null +++ b/applications/main/subghz/protocols/marantec.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MARANTEC_NAME "Marantec" + +typedef struct SubGhzProtocolDecoderMarantec SubGhzProtocolDecoderMarantec; +typedef struct SubGhzProtocolEncoderMarantec SubGhzProtocolEncoderMarantec; + +extern const SubGhzProtocolDecoder subghz_protocol_marantec_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_marantec_encoder; +extern const SubGhzProtocol subghz_protocol_marantec; + +/** + * Allocate SubGhzProtocolEncoderMarantec. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMarantec* pointer to a SubGhzProtocolEncoderMarantec instance + */ +void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMarantec. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + */ +void subghz_protocol_encoder_marantec_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + */ +void subghz_protocol_encoder_marantec_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_marantec_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMarantec. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMarantec* pointer to a SubGhzProtocolDecoderMarantec instance + */ +void* subghz_protocol_decoder_marantec_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + */ +void subghz_protocol_decoder_marantec_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + */ +void subghz_protocol_decoder_marantec_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_marantec_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_marantec_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param output Resulting text + */ +void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/megacode.c b/applications/main/subghz/protocols/megacode.c new file mode 100644 index 000000000..05b5b6894 --- /dev/null +++ b/applications/main/subghz/protocols/megacode.c @@ -0,0 +1,429 @@ +#include "megacode.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://wiki.cuvoodoo.info/doku.php?id=megacode + * https://wiki.cuvoodoo.info/lib/exe/fetch.php?media=megacode:megacode_1.pdf + * https://fccid.io/EF4ACP00872/Test-Report/Megacode-2-112615.pdf + * https://github.com/aaronsp777/megadecoder + * https://github.com/rjmendez/Linear_keyfob + * https://github.com/j07rdi/Linear_MegaCode_Garage_Remote + * + */ + +#define TAG "SubGhzProtocolMegaCode" + +static const SubGhzBlockConst subghz_protocol_megacode_const = { + .te_short = 1000, + .te_long = 1000, + .te_delta = 200, + .min_count_bit_for_found = 24, +}; + +struct SubGhzProtocolDecoderMegaCode { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + uint8_t last_bit; +}; + +struct SubGhzProtocolEncoderMegaCode { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + MegaCodeDecoderStepReset = 0, + MegaCodeDecoderStepFoundStartBit, + MegaCodeDecoderStepSaveDuration, + MegaCodeDecoderStepCheckDuration, +} MegaCodeDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_megacode_decoder = { + .alloc = subghz_protocol_decoder_megacode_alloc, + .free = subghz_protocol_decoder_megacode_free, + + .feed = subghz_protocol_decoder_megacode_feed, + .reset = subghz_protocol_decoder_megacode_reset, + + .get_hash_data = subghz_protocol_decoder_megacode_get_hash_data, + .serialize = subghz_protocol_decoder_megacode_serialize, + .deserialize = subghz_protocol_decoder_megacode_deserialize, + .get_string = subghz_protocol_decoder_megacode_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_megacode_encoder = { + .alloc = subghz_protocol_encoder_megacode_alloc, + .free = subghz_protocol_encoder_megacode_free, + + .deserialize = subghz_protocol_encoder_megacode_deserialize, + .stop = subghz_protocol_encoder_megacode_stop, + .yield = subghz_protocol_encoder_megacode_yield, +}; + +const SubGhzProtocol subghz_protocol_megacode = { + .name = SUBGHZ_PROTOCOL_MEGACODE_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_megacode_decoder, + .encoder = &subghz_protocol_megacode_encoder, +}; + +void* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMegaCode* instance = malloc(sizeof(SubGhzProtocolEncoderMegaCode)); + + instance->base.protocol = &subghz_protocol_megacode; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_megacode_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMegaCode* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMegaCode instance + * @return true On success + */ +static bool subghz_protocol_encoder_megacode_get_upload(SubGhzProtocolEncoderMegaCode* instance) { + furi_assert(instance); + uint8_t last_bit = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + /* + * Due to the nature of the protocol + * + * 00000 1 + * _____|-| = 1 becomes + * + * 00 1 000 + * __|-|___ = 0 becomes + * + * it's easier for us to generate an upload backwards + */ + + size_t index = size_upload - 1; + + // Send end level + instance->encoder.upload[index--] = + level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short); + if(bit_read(instance->generic.data, 0)) { + last_bit = 1; + } else { + last_bit = 0; + } + + //Send key data + for(uint8_t i = 1; i < instance->generic.data_count_bit; i++) { + if(bit_read(instance->generic.data, i)) { + //if bit 1 + instance->encoder.upload[index--] = level_duration_make( + false, + last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 5 : + (uint32_t)subghz_protocol_megacode_const.te_short * 2); + last_bit = 1; + } else { + //if bit 0 + instance->encoder.upload[index--] = level_duration_make( + false, + last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 8 : + (uint32_t)subghz_protocol_megacode_const.te_short * 5); + last_bit = 0; + } + instance->encoder.upload[index--] = + level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short); + } + + //Send PT_GUARD + if(bit_read(instance->generic.data, 0)) { + //if end bit 1 + instance->encoder.upload[index] = + level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 11); + } else { + //if end bit 1 + instance->encoder.upload[index] = + level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 14); + } + + return true; +} + +bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMegaCode* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_megacode_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_megacode_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_megacode_stop(void* context) { + SubGhzProtocolEncoderMegaCode* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_megacode_yield(void* context) { + SubGhzProtocolEncoderMegaCode* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_megacode_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMegaCode* instance = malloc(sizeof(SubGhzProtocolDecoderMegaCode)); + instance->base.protocol = &subghz_protocol_megacode; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_megacode_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + free(instance); +} + +void subghz_protocol_decoder_megacode_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + instance->decoder.parser_step = MegaCodeDecoderStepReset; +} + +void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + switch(instance->decoder.parser_step) { + case MegaCodeDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short * 13) < + subghz_protocol_megacode_const.te_delta * 17)) { //10..16ms + //Found header MegaCode + instance->decoder.parser_step = MegaCodeDecoderStepFoundStartBit; + } + break; + case MegaCodeDecoderStepFoundStartBit: + if(level && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) < + subghz_protocol_megacode_const.te_delta)) { + //Found start bit MegaCode + instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->last_bit = 1; + + } else { + instance->decoder.parser_step = MegaCodeDecoderStepReset; + } + break; + case MegaCodeDecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_megacode_const.te_short * 10)) { + instance->decoder.parser_step = MegaCodeDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_megacode_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + + if(!instance->last_bit) { + instance->decoder.te_last = duration - subghz_protocol_megacode_const.te_short * 3; + } else { + instance->decoder.te_last = duration; + } + instance->decoder.parser_step = MegaCodeDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = MegaCodeDecoderStepReset; + } + break; + case MegaCodeDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 5) < + subghz_protocol_megacode_const.te_delta * 5) && + (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) < + subghz_protocol_megacode_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->last_bit = 1; + instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 2) < + subghz_protocol_megacode_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) < + subghz_protocol_megacode_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->last_bit = 0; + instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration; + } else + instance->decoder.parser_step = MegaCodeDecoderStepReset; + } else { + instance->decoder.parser_step = MegaCodeDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_megacode_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * Short: 1000 µs + * Long: 1000 µs + * Gap: 11000 .. 14000 µs + * A Linear Megacode transmission consists of 24 bit frames starting with + * the most significant bit and ending with the least. Each of the 24 bit + * frames is 6 milliseconds wide and always contains a single 1 millisecond + * pulse. A frame with more than 1 pulse or a frame with no pulse is invalid + * and a receiver should reset and begin watching for another start bit. + * Start bit is always 1. + * + * + * Example (I created with my own remote): + * Remote “A” has the code “17316”, a Facility Code of “3”, and a single button. + * Start bit (S) = 1 + * Facility Code 3 (F) = 0011 + * Remote Code (Key) 17316 = 43A4 = 0100001110100100 + * Button (Btn) 1 = 001 + * S F Key Btn + * Result = 1|0011|0100001110100100|001 + * + * 00000 1 + * _____|-| = 1 becomes + * + * 00 1 000 + * __|-|___ = 0 becomes + * + * The device needs to transmit with a 9000 µs gap between retransmissions: + * 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001 + * 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001 + * wait 9000 µs + * 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001 + * 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001 + * + */ + if((instance->data >> 23) == 1) { + instance->serial = (instance->data >> 3) & 0xFFFF; + instance->btn = instance->data & 0b111; + instance->cnt = (instance->data >> 19) & 0b1111; + } else { + instance->serial = 0; + instance->btn = 0; + instance->cnt = 0; + } +} + +uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_megacode_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_megacode_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_megacode_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + subghz_protocol_megacode_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%06lX\r\n" + "Sn:0x%04lX - %lu\r\n" + "Facility:%lX Btn:%X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)instance->generic.data, + instance->generic.serial, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/megacode.h b/applications/main/subghz/protocols/megacode.h new file mode 100644 index 000000000..e31434fa3 --- /dev/null +++ b/applications/main/subghz/protocols/megacode.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MEGACODE_NAME "MegaCode" + +typedef struct SubGhzProtocolDecoderMegaCode SubGhzProtocolDecoderMegaCode; +typedef struct SubGhzProtocolEncoderMegaCode SubGhzProtocolEncoderMegaCode; + +extern const SubGhzProtocolDecoder subghz_protocol_megacode_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_megacode_encoder; +extern const SubGhzProtocol subghz_protocol_megacode; + +/** + * Allocate SubGhzProtocolEncoderMegaCode. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMegaCode* pointer to a SubGhzProtocolEncoderMegaCode instance + */ +void* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMegaCode. + * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance + */ +void subghz_protocol_encoder_megacode_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance + */ +void subghz_protocol_encoder_megacode_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_megacode_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMegaCode. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMegaCode* pointer to a SubGhzProtocolDecoderMegaCode instance + */ +void* subghz_protocol_decoder_megacode_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMegaCode. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + */ +void subghz_protocol_decoder_megacode_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMegaCode. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + */ +void subghz_protocol_decoder_megacode_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMegaCode. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_megacode_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMegaCode. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + * @param output Resulting text + */ +void subghz_protocol_decoder_megacode_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nero_radio.c b/applications/main/subghz/protocols/nero_radio.c new file mode 100644 index 000000000..c8126b1e1 --- /dev/null +++ b/applications/main/subghz/protocols/nero_radio.c @@ -0,0 +1,397 @@ +#include "nero_radio.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolNeroRadio" + +static const SubGhzBlockConst subghz_protocol_nero_radio_const = { + .te_short = 200, + .te_long = 400, + .te_delta = 80, + .min_count_bit_for_found = 56, +}; + +struct SubGhzProtocolDecoderNeroRadio { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderNeroRadio { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + NeroRadioDecoderStepReset = 0, + NeroRadioDecoderStepCheckPreambula, + NeroRadioDecoderStepSaveDuration, + NeroRadioDecoderStepCheckDuration, +} NeroRadioDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_nero_radio_decoder = { + .alloc = subghz_protocol_decoder_nero_radio_alloc, + .free = subghz_protocol_decoder_nero_radio_free, + + .feed = subghz_protocol_decoder_nero_radio_feed, + .reset = subghz_protocol_decoder_nero_radio_reset, + + .get_hash_data = subghz_protocol_decoder_nero_radio_get_hash_data, + .serialize = subghz_protocol_decoder_nero_radio_serialize, + .deserialize = subghz_protocol_decoder_nero_radio_deserialize, + .get_string = subghz_protocol_decoder_nero_radio_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_nero_radio_encoder = { + .alloc = subghz_protocol_encoder_nero_radio_alloc, + .free = subghz_protocol_encoder_nero_radio_free, + + .deserialize = subghz_protocol_encoder_nero_radio_deserialize, + .stop = subghz_protocol_encoder_nero_radio_stop, + .yield = subghz_protocol_encoder_nero_radio_yield, +}; + +const SubGhzProtocol subghz_protocol_nero_radio = { + .name = SUBGHZ_PROTOCOL_NERO_RADIO_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_nero_radio_decoder, + .encoder = &subghz_protocol_nero_radio_encoder, +}; + +void* subghz_protocol_encoder_nero_radio_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderNeroRadio* instance = malloc(sizeof(SubGhzProtocolEncoderNeroRadio)); + + instance->base.protocol = &subghz_protocol_nero_radio; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_nero_radio_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderNeroRadio* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderNeroRadio instance + * @return true On success + */ +static bool + subghz_protocol_encoder_nero_radio_get_upload(SubGhzProtocolEncoderNeroRadio* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = 49 * 2 + 2 + (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + for(uint8_t i = 0; i < 49; i++) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short); + } + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short * 4); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_long); + } + } + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37); + } + return true; +} + +bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderNeroRadio* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_nero_radio_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_nero_radio_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_nero_radio_stop(void* context) { + SubGhzProtocolEncoderNeroRadio* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_nero_radio_yield(void* context) { + SubGhzProtocolEncoderNeroRadio* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_nero_radio_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderNeroRadio* instance = malloc(sizeof(SubGhzProtocolDecoderNeroRadio)); + instance->base.protocol = &subghz_protocol_nero_radio; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_nero_radio_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + free(instance); +} + +void subghz_protocol_decoder_nero_radio_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + instance->decoder.parser_step = NeroRadioDecoderStepReset; +} + +void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + + switch(instance->decoder.parser_step) { + case NeroRadioDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta)) { + instance->decoder.parser_step = NeroRadioDecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + case NeroRadioDecoderStepCheckPreambula: + if(level) { + if((DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short * 4) < + subghz_protocol_nero_radio_const.te_delta)) { + instance->decoder.te_last = duration; + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + } else if( + DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta) { + if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta) { + // Found header + instance->header_count++; + break; + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short * 4) < + subghz_protocol_nero_radio_const.te_delta) { + // Found start bit + if(instance->header_count > 40) { + instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + break; + case NeroRadioDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = NeroRadioDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + break; + case NeroRadioDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_nero_radio_const.te_short * 10 + + subghz_protocol_nero_radio_const.te_delta * 2)) { + //Found stop bit + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_radio_const.te_long) < + subghz_protocol_nero_radio_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } + instance->decoder.parser_step = NeroRadioDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_nero_radio_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = NeroRadioDecoderStepReset; //-V1048 + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_long) < + subghz_protocol_nero_radio_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_radio_const.te_long) < + subghz_protocol_nero_radio_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_nero_radio_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_nero_radio_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + uint32_t code_found_reverse_hi = code_found_reverse >> 32; + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Yek:0x%lX%08lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + code_found_reverse_lo); +} diff --git a/applications/main/subghz/protocols/nero_radio.h b/applications/main/subghz/protocols/nero_radio.h new file mode 100644 index 000000000..361da6173 --- /dev/null +++ b/applications/main/subghz/protocols/nero_radio.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_NERO_RADIO_NAME "Nero Radio" + +typedef struct SubGhzProtocolDecoderNeroRadio SubGhzProtocolDecoderNeroRadio; +typedef struct SubGhzProtocolEncoderNeroRadio SubGhzProtocolEncoderNeroRadio; + +extern const SubGhzProtocolDecoder subghz_protocol_nero_radio_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_nero_radio_encoder; +extern const SubGhzProtocol subghz_protocol_nero_radio; + +/** + * Allocate SubGhzProtocolEncoderNeroRadio. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderNeroRadio* pointer to a SubGhzProtocolEncoderNeroRadio instance + */ +void* subghz_protocol_encoder_nero_radio_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderNeroRadio. + * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance + */ +void subghz_protocol_encoder_nero_radio_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance + */ +void subghz_protocol_encoder_nero_radio_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_nero_radio_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderNeroRadio. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderNeroRadio* pointer to a SubGhzProtocolDecoderNeroRadio instance + */ +void* subghz_protocol_decoder_nero_radio_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderNeroRadio. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + */ +void subghz_protocol_decoder_nero_radio_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderNeroRadio. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + */ +void subghz_protocol_decoder_nero_radio_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderNeroRadio. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_nero_radio_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderNeroRadio. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + * @param output Resulting text + */ +void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nero_sketch.c b/applications/main/subghz/protocols/nero_sketch.c new file mode 100644 index 000000000..b124b717b --- /dev/null +++ b/applications/main/subghz/protocols/nero_sketch.c @@ -0,0 +1,382 @@ +#include "nero_sketch.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolNeroSketch" + +static const SubGhzBlockConst subghz_protocol_nero_sketch_const = { + .te_short = 330, + .te_long = 660, + .te_delta = 150, + .min_count_bit_for_found = 40, +}; + +struct SubGhzProtocolDecoderNeroSketch { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderNeroSketch { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + NeroSketchDecoderStepReset = 0, + NeroSketchDecoderStepCheckPreambula, + NeroSketchDecoderStepSaveDuration, + NeroSketchDecoderStepCheckDuration, +} NeroSketchDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_nero_sketch_decoder = { + .alloc = subghz_protocol_decoder_nero_sketch_alloc, + .free = subghz_protocol_decoder_nero_sketch_free, + + .feed = subghz_protocol_decoder_nero_sketch_feed, + .reset = subghz_protocol_decoder_nero_sketch_reset, + + .get_hash_data = subghz_protocol_decoder_nero_sketch_get_hash_data, + .serialize = subghz_protocol_decoder_nero_sketch_serialize, + .deserialize = subghz_protocol_decoder_nero_sketch_deserialize, + .get_string = subghz_protocol_decoder_nero_sketch_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_nero_sketch_encoder = { + .alloc = subghz_protocol_encoder_nero_sketch_alloc, + .free = subghz_protocol_encoder_nero_sketch_free, + + .deserialize = subghz_protocol_encoder_nero_sketch_deserialize, + .stop = subghz_protocol_encoder_nero_sketch_stop, + .yield = subghz_protocol_encoder_nero_sketch_yield, +}; + +const SubGhzProtocol subghz_protocol_nero_sketch = { + .name = SUBGHZ_PROTOCOL_NERO_SKETCH_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_nero_sketch_decoder, + .encoder = &subghz_protocol_nero_sketch_encoder, +}; + +void* subghz_protocol_encoder_nero_sketch_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderNeroSketch* instance = malloc(sizeof(SubGhzProtocolEncoderNeroSketch)); + + instance->base.protocol = &subghz_protocol_nero_sketch; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_nero_sketch_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderNeroSketch* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderNeroSketch instance + * @return true On success + */ +static bool + subghz_protocol_encoder_nero_sketch_get_upload(SubGhzProtocolEncoderNeroSketch* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = 47 * 2 + 2 + (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + for(uint8_t i = 0; i < 47; i++) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + } + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short * 4); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_long); + } + } + + //Send stop bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + + return true; +} + +bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderNeroSketch* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_nero_sketch_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_nero_sketch_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_nero_sketch_stop(void* context) { + SubGhzProtocolEncoderNeroSketch* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_nero_sketch_yield(void* context) { + SubGhzProtocolEncoderNeroSketch* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_nero_sketch_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderNeroSketch* instance = malloc(sizeof(SubGhzProtocolDecoderNeroSketch)); + instance->base.protocol = &subghz_protocol_nero_sketch; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_nero_sketch_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + free(instance); +} + +void subghz_protocol_decoder_nero_sketch_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + instance->decoder.parser_step = NeroSketchDecoderStepReset; +} + +void subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + + switch(instance->decoder.parser_step) { + case NeroSketchDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta)) { + instance->decoder.parser_step = NeroSketchDecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + case NeroSketchDecoderStepCheckPreambula: + if(level) { + if((DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short * 4) < + subghz_protocol_nero_sketch_const.te_delta)) { + instance->decoder.te_last = duration; + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + } else if( + DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta) { + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta) { + // Found header + instance->header_count++; + break; + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short * 4) < + subghz_protocol_nero_sketch_const.te_delta) { + // Found start bit + if(instance->header_count > 40) { + instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + break; + case NeroSketchDecoderStepSaveDuration: + if(level) { + if(duration >= (subghz_protocol_nero_sketch_const.te_short * 2 + + subghz_protocol_nero_sketch_const.te_delta * 2)) { + //Found stop bit + instance->decoder.parser_step = NeroSketchDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_nero_sketch_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = NeroSketchDecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + break; + case NeroSketchDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_long) < + subghz_protocol_nero_sketch_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_long) < + subghz_protocol_nero_sketch_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_nero_sketch_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_nero_sketch_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_nero_sketch_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + uint32_t code_found_reverse_hi = code_found_reverse >> 32; + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Yek:0x%lX%08lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + code_found_reverse_lo); +} diff --git a/applications/main/subghz/protocols/nero_sketch.h b/applications/main/subghz/protocols/nero_sketch.h new file mode 100644 index 000000000..ac87fb00a --- /dev/null +++ b/applications/main/subghz/protocols/nero_sketch.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_NERO_SKETCH_NAME "Nero Sketch" + +typedef struct SubGhzProtocolDecoderNeroSketch SubGhzProtocolDecoderNeroSketch; +typedef struct SubGhzProtocolEncoderNeroSketch SubGhzProtocolEncoderNeroSketch; + +extern const SubGhzProtocolDecoder subghz_protocol_nero_sketch_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_nero_sketch_encoder; +extern const SubGhzProtocol subghz_protocol_nero_sketch; + +/** + * Allocate SubGhzProtocolEncoderNeroSketch. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderNeroSketch* pointer to a SubGhzProtocolEncoderNeroSketch instance + */ +void* subghz_protocol_encoder_nero_sketch_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderNeroSketch. + * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance + */ +void subghz_protocol_encoder_nero_sketch_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance + */ +void subghz_protocol_encoder_nero_sketch_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_nero_sketch_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderNeroSketch. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderNeroSketch* pointer to a SubGhzProtocolDecoderNeroSketch instance + */ +void* subghz_protocol_decoder_nero_sketch_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderNeroSketch. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + */ +void subghz_protocol_decoder_nero_sketch_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderNeroSketch. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + */ +void subghz_protocol_decoder_nero_sketch_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderNeroSketch. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_nero_sketch_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderNeroSketch. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + * @param output Resulting text + */ +void subghz_protocol_decoder_nero_sketch_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nice_flo.c b/applications/main/subghz/protocols/nice_flo.c new file mode 100644 index 000000000..a57d5f4da --- /dev/null +++ b/applications/main/subghz/protocols/nice_flo.c @@ -0,0 +1,330 @@ +#include "nice_flo.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolNiceFLO" + +static const SubGhzBlockConst subghz_protocol_nice_flo_const = { + .te_short = 700, + .te_long = 1400, + .te_delta = 200, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderNiceFlo { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderNiceFlo { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + NiceFloDecoderStepReset = 0, + NiceFloDecoderStepFoundStartBit, + NiceFloDecoderStepSaveDuration, + NiceFloDecoderStepCheckDuration, +} NiceFloDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_nice_flo_decoder = { + .alloc = subghz_protocol_decoder_nice_flo_alloc, + .free = subghz_protocol_decoder_nice_flo_free, + + .feed = subghz_protocol_decoder_nice_flo_feed, + .reset = subghz_protocol_decoder_nice_flo_reset, + + .get_hash_data = subghz_protocol_decoder_nice_flo_get_hash_data, + .serialize = subghz_protocol_decoder_nice_flo_serialize, + .deserialize = subghz_protocol_decoder_nice_flo_deserialize, + .get_string = subghz_protocol_decoder_nice_flo_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_nice_flo_encoder = { + .alloc = subghz_protocol_encoder_nice_flo_alloc, + .free = subghz_protocol_encoder_nice_flo_free, + + .deserialize = subghz_protocol_encoder_nice_flo_deserialize, + .stop = subghz_protocol_encoder_nice_flo_stop, + .yield = subghz_protocol_encoder_nice_flo_yield, +}; + +const SubGhzProtocol subghz_protocol_nice_flo = { + .name = SUBGHZ_PROTOCOL_NICE_FLO_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_nice_flo_decoder, + .encoder = &subghz_protocol_nice_flo_encoder, +}; + +void* subghz_protocol_encoder_nice_flo_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderNiceFlo* instance = malloc(sizeof(SubGhzProtocolEncoderNiceFlo)); + + instance->base.protocol = &subghz_protocol_nice_flo; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop) + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_nice_flo_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderNiceFlo* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderNiceFlo instance + * @return true On success + */ +static bool subghz_protocol_encoder_nice_flo_get_upload(SubGhzProtocolEncoderNiceFlo* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_short * 36); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderNiceFlo* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if((instance->generic.data_count_bit < + subghz_protocol_nice_flo_const.min_count_bit_for_found) || + (instance->generic.data_count_bit > + 2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_nice_flo_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_nice_flo_stop(void* context) { + SubGhzProtocolEncoderNiceFlo* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_nice_flo_yield(void* context) { + SubGhzProtocolEncoderNiceFlo* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_nice_flo_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderNiceFlo* instance = malloc(sizeof(SubGhzProtocolDecoderNiceFlo)); + instance->base.protocol = &subghz_protocol_nice_flo; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_nice_flo_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + free(instance); +} + +void subghz_protocol_decoder_nice_flo_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + instance->decoder.parser_step = NiceFloDecoderStepReset; +} + +void subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + + switch(instance->decoder.parser_step) { + case NiceFloDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short * 36) < + subghz_protocol_nice_flo_const.te_delta * 36)) { + //Found header Nice Flo + instance->decoder.parser_step = NiceFloDecoderStepFoundStartBit; + } + break; + case NiceFloDecoderStepFoundStartBit: + if(!level) { + break; + } else if( + DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short) < + subghz_protocol_nice_flo_const.te_delta) { + //Found start bit Nice Flo + instance->decoder.parser_step = NiceFloDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = NiceFloDecoderStepReset; + } + break; + case NiceFloDecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_nice_flo_const.te_short * 4)) { + instance->decoder.parser_step = NiceFloDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_nice_flo_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = NiceFloDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = NiceFloDecoderStepReset; + } + break; + case NiceFloDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nice_flo_const.te_short) < + subghz_protocol_nice_flo_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_long) < + subghz_protocol_nice_flo_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = NiceFloDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nice_flo_const.te_long) < + subghz_protocol_nice_flo_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short) < + subghz_protocol_nice_flo_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = NiceFloDecoderStepSaveDuration; + } else + instance->decoder.parser_step = NiceFloDecoderStepReset; + } else { + instance->decoder.parser_step = NiceFloDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_nice_flo_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if((instance->generic.data_count_bit < + subghz_protocol_nice_flo_const.min_count_bit_for_found) || + (instance->generic.data_count_bit > + 2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_nice_flo_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Yek:0x%08lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_lo, + code_found_reverse_lo); +} diff --git a/applications/main/subghz/protocols/nice_flo.h b/applications/main/subghz/protocols/nice_flo.h new file mode 100644 index 000000000..e382e6146 --- /dev/null +++ b/applications/main/subghz/protocols/nice_flo.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_NICE_FLO_NAME "Nice FLO" + +typedef struct SubGhzProtocolDecoderNiceFlo SubGhzProtocolDecoderNiceFlo; +typedef struct SubGhzProtocolEncoderNiceFlo SubGhzProtocolEncoderNiceFlo; + +extern const SubGhzProtocolDecoder subghz_protocol_nice_flo_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_nice_flo_encoder; +extern const SubGhzProtocol subghz_protocol_nice_flo; + +/** + * Allocate SubGhzProtocolEncoderNiceFlo. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderNiceFlo* pointer to a SubGhzProtocolEncoderNiceFlo instance + */ +void* subghz_protocol_encoder_nice_flo_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderNiceFlo. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance + */ +void subghz_protocol_encoder_nice_flo_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance + */ +void subghz_protocol_encoder_nice_flo_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_nice_flo_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderNiceFlo. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderNiceFlo* pointer to a SubGhzProtocolDecoderNiceFlo instance + */ +void* subghz_protocol_decoder_nice_flo_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderNiceFlo. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + */ +void subghz_protocol_decoder_nice_flo_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderNiceFlo. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + */ +void subghz_protocol_decoder_nice_flo_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderNiceFlo. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_nice_flo_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderNiceFlo. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + * @param output Resulting text + */ +void subghz_protocol_decoder_nice_flo_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nice_flor_s.c b/applications/main/subghz/protocols/nice_flor_s.c new file mode 100644 index 000000000..6447676cc --- /dev/null +++ b/applications/main/subghz/protocols/nice_flor_s.c @@ -0,0 +1,694 @@ +#include "nice_flor_s.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * https://phreakerclub.com/1615 + * https://phreakerclub.com/forum/showthread.php?t=2360 + * https://vrtp.ru/index.php?showtopic=27867 + */ + +#define TAG "SubGhzProtocolNiceFlorS" + +#define NICE_ONE_COUNT_BIT 72 +#define NICE_ONE_NAME "Nice One" + +static const SubGhzBlockConst subghz_protocol_nice_flor_s_const = { + .te_short = 500, + .te_long = 1000, + .te_delta = 300, + .min_count_bit_for_found = 52, +}; + +struct SubGhzProtocolDecoderNiceFlorS { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + const char* nice_flor_s_rainbow_table_file_name; + uint64_t data; +}; + +struct SubGhzProtocolEncoderNiceFlorS { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + const char* nice_flor_s_rainbow_table_file_name; +}; + +typedef enum { + NiceFlorSDecoderStepReset = 0, + NiceFlorSDecoderStepCheckHeader, + NiceFlorSDecoderStepFoundHeader, + NiceFlorSDecoderStepSaveDuration, + NiceFlorSDecoderStepCheckDuration, +} NiceFlorSDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_nice_flor_s_decoder = { + .alloc = subghz_protocol_decoder_nice_flor_s_alloc, + .free = subghz_protocol_decoder_nice_flor_s_free, + + .feed = subghz_protocol_decoder_nice_flor_s_feed, + .reset = subghz_protocol_decoder_nice_flor_s_reset, + + .get_hash_data = subghz_protocol_decoder_nice_flor_s_get_hash_data, + .serialize = subghz_protocol_decoder_nice_flor_s_serialize, + .deserialize = subghz_protocol_decoder_nice_flor_s_deserialize, + .get_string = subghz_protocol_decoder_nice_flor_s_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_nice_flor_s_encoder = { + .alloc = subghz_protocol_encoder_nice_flor_s_alloc, + .free = subghz_protocol_encoder_nice_flor_s_free, + + .deserialize = subghz_protocol_encoder_nice_flor_s_deserialize, + .stop = subghz_protocol_encoder_nice_flor_s_stop, + .yield = subghz_protocol_encoder_nice_flor_s_yield, +}; + +const SubGhzProtocol subghz_protocol_nice_flor_s = { + .name = SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_nice_flor_s_decoder, + .encoder = &subghz_protocol_nice_flor_s_encoder, +}; + +static void subghz_protocol_nice_flor_s_remote_controller( + SubGhzBlockGeneric* instance, + const char* file_name); + +void* subghz_protocol_encoder_nice_flor_s_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderNiceFlorS* instance = malloc(sizeof(SubGhzProtocolEncoderNiceFlorS)); + + instance->base.protocol = &subghz_protocol_nice_flor_s; + instance->generic.protocol_name = instance->base.protocol->name; + instance->nice_flor_s_rainbow_table_file_name = + subghz_environment_get_nice_flor_s_rainbow_table_file_name(environment); + if(instance->nice_flor_s_rainbow_table_file_name) { + FURI_LOG_D( + TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name); + } + instance->encoder.repeat = 10; + instance->encoder.size_upload = 1728; //wrong!! upload 186*16 = 2976 - actual size about 1728 + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_nice_flor_s_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderNiceFlorS* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderNiceFlorS instance + * @return true On success + */ +static void subghz_protocol_encoder_nice_flor_s_get_upload( + SubGhzProtocolEncoderNiceFlorS* instance, + uint8_t btn, + const char* file_name) { + furi_assert(instance); + size_t index = 0; + btn = instance->generic.btn; + + size_t size_upload = ((instance->generic.data_count_bit * 2) + ((37 + 2 + 2) * 2) * 16); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + } else { + instance->encoder.size_upload = size_upload; + } + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt; + uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt(decrypt, file_name); + + for(int i = 0; i < 16; i++) { + static const uint64_t loops[16] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}; + + uint8_t byte; + + byte = btn << 4 | (0xF ^ btn ^ loops[i]); + instance->generic.data = (uint64_t)byte << 44 | enc_part; + + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 37); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_nice_flor_s_const.te_long); + } + } + //Send stop bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); + //instance->encoder.upload[index++] = + //level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); + } + instance->encoder.size_upload = index; +} + +bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderNiceFlorS* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_nice_flor_s_remote_controller( + &instance->generic, instance->nice_flor_s_rainbow_table_file_name); + subghz_protocol_encoder_nice_flor_s_get_upload( + instance, instance->generic.btn, instance->nice_flor_s_rainbow_table_file_name); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_nice_flor_s_stop(void* context) { + SubGhzProtocolEncoderNiceFlorS* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_nice_flor_s_yield(void* context) { + SubGhzProtocolEncoderNiceFlorS* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10 +// * @return crc +// */ +// static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) { +// uint8_t crc = 0; +// uint8_t crc_data = 0xff; +// for(uint8_t i = 4; i < 68; i++) { +// if(subghz_protocol_blocks_get_bit_array(p, i)) { +// crc = crc_data ^ 1; +// } else { +// crc = crc_data; +// } +// crc_data >>= 1; +// if((crc & 0x01)) { +// crc_data ^= 0x97; +// } +// } +// crc = 0; +// for(uint8_t i = 0; i < 8; i++) { +// crc <<= 1; +// if((crc_data >> i) & 0x01) crc = crc | 1; +// } +// return crc; +// } + +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX +// * @param num_parcel parcel number 0..15 +// * @param hold_bit 0 - the button was only pressed, 1 - the button was held down +// */ +// static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) { +// uint8_t k = 0; +// uint8_t crc = 0; +// p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4); +// if(num_parcel < 4) { +// k = 0x8f; +// } else { +// k = 0x80; +// } + +// if(!hold_bit) { +// hold_bit = 0; +// } else { +// hold_bit = 0x10; +// } +// k = num_parcel ^ k; +// p[7] = k; +// p[8] = hold_bit ^ (k << 4); + +// crc = subghz_protocol_nice_one_crc(p); + +// p[8] |= crc >> 4; +// p[9] = crc << 4; +// } + +/** + * Read bytes from rainbow table + * @param file_name Full path to rainbow table the file + * @param address Byte address in file + * @return data + */ +static uint8_t + subghz_protocol_nice_flor_s_get_byte_in_file(const char* file_name, uint32_t address) { + if(!file_name) return 0; + + uint8_t buffer[1] = {0}; + if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint8_t))) { + return buffer[0]; + } else { + return 0; + } +} + +static inline void subghz_protocol_decoder_nice_flor_s_magic_xor(uint8_t* p, uint8_t k) { + for(uint8_t i = 1; i < 6; i++) { + p[i] ^= k; + } +} + +uint64_t subghz_protocol_nice_flor_s_encrypt(uint64_t data, const char* file_name) { + uint8_t* p = (uint8_t*)&data; + + uint8_t k = 0; + for(uint8_t y = 0; y < 2; y++) { + k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] & 0x1f); + subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); + + p[5] &= 0x0f; + p[0] ^= k & 0xe0; + k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] >> 3) + 0x25; + subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); + + p[5] &= 0x0f; + p[0] ^= k & 0x7; + if(y == 0) { + k = p[0]; + p[0] = p[1]; + p[1] = k; + } + } + + p[5] = ~p[5] & 0x0f; + k = ~p[4]; + p[4] = ~p[0]; + p[0] = ~p[2]; + p[2] = k; + k = ~p[3]; + p[3] = ~p[1]; + p[1] = k; + + return data; +} + +static uint64_t + subghz_protocol_nice_flor_s_decrypt(SubGhzBlockGeneric* instance, const char* file_name) { + furi_assert(instance); + uint64_t data = instance->data; + uint8_t* p = (uint8_t*)&data; + + uint8_t k = 0; + + k = ~p[4]; + p[5] = ~p[5]; + p[4] = ~p[2]; + p[2] = ~p[0]; + p[0] = k; + k = ~p[3]; + p[3] = ~p[1]; + p[1] = k; + + for(uint8_t y = 0; y < 2; y++) { + k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] >> 3) + 0x25; + subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); + + p[5] &= 0x0f; + p[0] ^= k & 0x7; + k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] & 0x1f); + subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); + + p[5] &= 0x0f; + p[0] ^= k & 0xe0; + + if(y == 0) { + k = p[0]; + p[0] = p[1]; + p[1] = k; + } + } + + return data; +} + +bool subghz_protocol_nice_flor_s_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderNiceFlorS* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 52; + uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt; + uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt( + decrypt, instance->nice_flor_s_rainbow_table_file_name); + uint8_t byte = btn << 4 | (0xF ^ btn ^ 0x3); + instance->generic.data = (uint64_t)byte << 44 | enc_part; + + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + return res; +} + +void* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderNiceFlorS* instance = malloc(sizeof(SubGhzProtocolDecoderNiceFlorS)); + instance->base.protocol = &subghz_protocol_nice_flor_s; + instance->generic.protocol_name = instance->base.protocol->name; + instance->nice_flor_s_rainbow_table_file_name = + subghz_environment_get_nice_flor_s_rainbow_table_file_name(environment); + if(instance->nice_flor_s_rainbow_table_file_name) { + FURI_LOG_D( + TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name); + } + return instance; +} + +void subghz_protocol_decoder_nice_flor_s_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + instance->nice_flor_s_rainbow_table_file_name = NULL; + free(instance); +} + +void subghz_protocol_decoder_nice_flor_s_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + instance->decoder.parser_step = NiceFlorSDecoderStepReset; +} + +void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + + switch(instance->decoder.parser_step) { + case NiceFlorSDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 38) < + subghz_protocol_nice_flor_s_const.te_delta * 38)) { + //Found start header Nice Flor-S + instance->decoder.parser_step = NiceFlorSDecoderStepCheckHeader; + } + break; + case NiceFlorSDecoderStepCheckHeader: + if((level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) < + subghz_protocol_nice_flor_s_const.te_delta * 3)) { + //Found next header Nice Flor-S + instance->decoder.parser_step = NiceFlorSDecoderStepFoundHeader; + } else { + instance->decoder.parser_step = NiceFlorSDecoderStepReset; + } + break; + case NiceFlorSDecoderStepFoundHeader: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) < + subghz_protocol_nice_flor_s_const.te_delta * 3)) { + //Found header Nice Flor-S + instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = NiceFlorSDecoderStepReset; + } + break; + case NiceFlorSDecoderStepSaveDuration: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) < + subghz_protocol_nice_flor_s_const.te_delta) { + //Found STOP bit + instance->decoder.parser_step = NiceFlorSDecoderStepReset; + if((instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == NICE_ONE_COUNT_BIT)) { + instance->generic.data = instance->data; + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = instance->generic.data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } else { + //save interval + instance->decoder.te_last = duration; + instance->decoder.parser_step = NiceFlorSDecoderStepCheckDuration; + } + } + break; + case NiceFlorSDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nice_flor_s_const.te_short) < + subghz_protocol_nice_flor_s_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_long) < + subghz_protocol_nice_flor_s_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nice_flor_s_const.te_long) < + subghz_protocol_nice_flor_s_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short) < + subghz_protocol_nice_flor_s_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration; + } else + instance->decoder.parser_step = NiceFlorSDecoderStepReset; + } else { + instance->decoder.parser_step = NiceFlorSDecoderStepReset; + } + if(instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_nice_flor_s_remote_controller( + SubGhzBlockGeneric* instance, + const char* file_name) { + /* + * Protocol Nice Flor-S + * Packet format Nice Flor-s: START-P0-P1-P2-P3-P4-P5-P6-P7-STOP + * P0 (4-bit) - button positional code - 1:0x1, 2:0x2, 3:0x4, 4:0x8; + * P1 (4-bit) - batch repetition number, calculated by the formula: + * P1 = 0xF ^ P0 ^ n; where n changes from 1 to 15, then 0, and then in a circle + * key 1: {0xE,0xF,0xC,0xD,0xA,0xB,0x8,0x9,0x6,0x7,0x4,0x5,0x2,0x3,0x0,0x1}; + * key 2: {0xD,0xC,0xF,0xE,0x9,0x8,0xB,0xA,0x5,0x4,0x7,0x6,0x1,0x0,0x3,0x2}; + * key 3: {0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC,0x3,0x2,0x1,0x0,0x7,0x6,0x5,0x4}; + * key 4: {0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8}; + * P2 (4-bit) - part of the serial number, P2 = (K ^ S3) & 0xF; + * P3 (byte) - the major part of the encrypted index + * P4 (byte) - the low-order part of the encrypted index + * P5 (byte) - part of the serial number, P5 = K ^ S2; + * P6 (byte) - part of the serial number, P6 = K ^ S1; + * P7 (byte) - part of the serial number, P7 = K ^ S0; + * K (byte) - depends on P3 and P4, K = Fk(P3, P4); + * S3,S2,S1,S0 - serial number of the console 28 bit. + * + * data => 0x1c5783607f7b3 key serial cnt + * decrypt => 0x10436c6820444 => 0x1 0436c682 0444 + * + * Protocol Nice One + * Generally repeats the Nice Flor-S protocol, but there are a few changes + * Packet format first 52 bytes repeat Nice Flor-S protocol + * The additional 20 bytes contain the code of the pressed button, + * the button hold bit and the CRC of the entire message. + * START-P0-P1-P2-P3-P4-P5-P6-P7-P8-P9-P10-STOP + * P7 (byte) - if (n<4) k=0x8f : k=0x80; P7= k^n; + * P8 (byte) - if (hold bit) b=0x00 : b=0x10; P8= b^(k<<4) | 4 hi bit crc + * P10 (4-bit) - 4 lo bit crc + * key+b crc + * data => 0x1724A7D9A522F 899 D6 hold bit = 0 - just pressed the button + * data => 0x1424A7D9A522F 8AB 03 hold bit = 1 - button hold + * + * A small button hold counter (0..15) is stored between each press, + * i.e. if 1 press of the button stops counter 6, then the next press + * of the button will start from the value 7 (hold bit = 0), 8 (hold bit = 1)... + * further up to 15 with overflow + * + */ + if(!file_name) { + instance->cnt = 0; + instance->serial = 0; + instance->btn = 0; + } else { + uint64_t decrypt = subghz_protocol_nice_flor_s_decrypt(instance, file_name); + instance->cnt = decrypt & 0xFFFF; + instance->serial = (decrypt >> 16) & 0xFFFFFFF; + instance->btn = (decrypt >> 48) & 0xF; + } +} + +uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_nice_flor_s_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(res && + !flipper_format_write_uint32(flipper_format, "Data", (uint32_t*)&instance->data, 1)) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + } + return res; +} + +bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if((instance->generic.data_count_bit != + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != NICE_ONE_COUNT_BIT)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint32_t temp = 0; + if(!flipper_format_read_uint32(flipper_format, "Data", (uint32_t*)&temp, 1)) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + instance->data = (uint64_t)temp; + } + + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + + subghz_protocol_nice_flor_s_remote_controller( + &instance->generic, instance->nice_flor_s_rainbow_table_file_name); + + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX%llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + NICE_ONE_NAME, + instance->generic.data_count_bit, + instance->generic.data, + instance->data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } else { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } +} diff --git a/applications/main/subghz/protocols/nice_flor_s.h b/applications/main/subghz/protocols/nice_flor_s.h new file mode 100644 index 000000000..e333fc979 --- /dev/null +++ b/applications/main/subghz/protocols/nice_flor_s.h @@ -0,0 +1,127 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME "Nice FloR-S" + +typedef struct SubGhzProtocolDecoderNiceFlorS SubGhzProtocolDecoderNiceFlorS; +typedef struct SubGhzProtocolEncoderNiceFlorS SubGhzProtocolEncoderNiceFlorS; + +extern const SubGhzProtocolDecoder subghz_protocol_nice_flor_s_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_nice_flor_s_encoder; +extern const SubGhzProtocol subghz_protocol_nice_flor_s; + +/** + * Allocate SubGhzProtocolEncoderNiceFlorS. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderNiceFlorS* pointer to a SubGhzProtocolEncoderNiceFlorS instance + */ +void* subghz_protocol_encoder_nice_flor_s_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderNiceFlorS. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance + */ +void subghz_protocol_encoder_nice_flor_s_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance + */ +void subghz_protocol_encoder_nice_flor_s_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_nice_flor_s_yield(void* context); + +uint64_t subghz_protocol_nice_flor_s_encrypt(uint64_t data, const char* file_name); + +/** + * New remote generation. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number + * @param btn Button number, 4 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_nice_flor_s_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Allocate SubGhzProtocolDecoderNiceFlorS. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderNiceFlorS* pointer to a SubGhzProtocolDecoderNiceFlorS instance + */ +void* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderNiceFlorS. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + */ +void subghz_protocol_decoder_nice_flor_s_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderNiceFlorS. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + */ +void subghz_protocol_decoder_nice_flor_s_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderNiceFlorS. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_nice_flor_s_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderNiceFlorS. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + * @param output Resulting text + */ +void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/phoenix_v2.c b/applications/main/subghz/protocols/phoenix_v2.c new file mode 100644 index 000000000..b3d6f1e98 --- /dev/null +++ b/applications/main/subghz/protocols/phoenix_v2.c @@ -0,0 +1,339 @@ +#include "phoenix_v2.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolPhoenix_V2" + +//transmission only static mode + +static const SubGhzBlockConst subghz_protocol_phoenix_v2_const = { + .te_short = 427, + .te_long = 853, + .te_delta = 100, + .min_count_bit_for_found = 52, +}; + +struct SubGhzProtocolDecoderPhoenix_V2 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderPhoenix_V2 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Phoenix_V2DecoderStepReset = 0, + Phoenix_V2DecoderStepFoundStartBit, + Phoenix_V2DecoderStepSaveDuration, + Phoenix_V2DecoderStepCheckDuration, +} Phoenix_V2DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder = { + .alloc = subghz_protocol_decoder_phoenix_v2_alloc, + .free = subghz_protocol_decoder_phoenix_v2_free, + + .feed = subghz_protocol_decoder_phoenix_v2_feed, + .reset = subghz_protocol_decoder_phoenix_v2_reset, + + .get_hash_data = subghz_protocol_decoder_phoenix_v2_get_hash_data, + .serialize = subghz_protocol_decoder_phoenix_v2_serialize, + .deserialize = subghz_protocol_decoder_phoenix_v2_deserialize, + .get_string = subghz_protocol_decoder_phoenix_v2_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder = { + .alloc = subghz_protocol_encoder_phoenix_v2_alloc, + .free = subghz_protocol_encoder_phoenix_v2_free, + + .deserialize = subghz_protocol_encoder_phoenix_v2_deserialize, + .stop = subghz_protocol_encoder_phoenix_v2_stop, + .yield = subghz_protocol_encoder_phoenix_v2_yield, +}; + +const SubGhzProtocol subghz_protocol_phoenix_v2 = { + .name = SUBGHZ_PROTOCOL_PHOENIX_V2_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_phoenix_v2_decoder, + .encoder = &subghz_protocol_phoenix_v2_encoder, +}; + +void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolEncoderPhoenix_V2)); + + instance->base.protocol = &subghz_protocol_phoenix_v2; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_phoenix_v2_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderPhoenix_V2* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_phoenix_v2_get_upload(SubGhzProtocolEncoderPhoenix_V2* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 60); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 6); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(!bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderPhoenix_V2* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_phoenix_v2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_phoenix_v2_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_phoenix_v2_stop(void* context) { + SubGhzProtocolEncoderPhoenix_V2* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context) { + SubGhzProtocolEncoderPhoenix_V2* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_phoenix_v2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolDecoderPhoenix_V2)); + instance->base.protocol = &subghz_protocol_phoenix_v2; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_phoenix_v2_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + free(instance); +} + +void subghz_protocol_decoder_phoenix_v2_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; +} + +void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + + switch(instance->decoder.parser_step) { + case Phoenix_V2DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short * 60) < + subghz_protocol_phoenix_v2_const.te_delta * 30)) { + //Found Preambula + instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit; + } + break; + case Phoenix_V2DecoderStepFoundStartBit: + if(level && ((DURATION_DIFF(duration, (subghz_protocol_phoenix_v2_const.te_short * 6)) < + subghz_protocol_phoenix_v2_const.te_delta * 4))) { + //Found start bit + instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; + } + break; + case Phoenix_V2DecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_phoenix_v2_const.te_short * 10 + + subghz_protocol_phoenix_v2_const.te_delta)) { + instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_phoenix_v2_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Phoenix_V2DecoderStepCheckDuration; + } + } + break; + case Phoenix_V2DecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_short) < + subghz_protocol_phoenix_v2_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_long) < + subghz_protocol_phoenix_v2_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_long) < + subghz_protocol_phoenix_v2_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short) < + subghz_protocol_phoenix_v2_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; + } + } else { + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance) { + uint64_t data_rev = + subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit + 4); + instance->serial = data_rev & 0xFFFFFFFF; + instance->cnt = (data_rev >> 40) & 0xFFFF; + instance->btn = (data_rev >> 32) & 0xF; +} + +uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_phoenix_v2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_phoenix_v2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%02lX%08lX\r\n" + "Sn:0x%07lX \r\n" + "Btn:%X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/phoenix_v2.h b/applications/main/subghz/protocols/phoenix_v2.h new file mode 100644 index 000000000..48487535e --- /dev/null +++ b/applications/main/subghz/protocols/phoenix_v2.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_PHOENIX_V2_NAME "Phoenix_V2" + +typedef struct SubGhzProtocolDecoderPhoenix_V2 SubGhzProtocolDecoderPhoenix_V2; +typedef struct SubGhzProtocolEncoderPhoenix_V2 SubGhzProtocolEncoderPhoenix_V2; + +extern const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder; +extern const SubGhzProtocol subghz_protocol_phoenix_v2; + +/** + * Allocate SubGhzProtocolEncoderPhoenix_V2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderPhoenix_V2* pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + */ +void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + */ +void subghz_protocol_encoder_phoenix_v2_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + */ +void subghz_protocol_encoder_phoenix_v2_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderPhoenix_V2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderPhoenix_V2* pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + */ +void* subghz_protocol_decoder_phoenix_v2_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + */ +void subghz_protocol_decoder_phoenix_v2_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + */ +void subghz_protocol_decoder_phoenix_v2_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_phoenix_v2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/power_smart.c b/applications/main/subghz/protocols/power_smart.c new file mode 100644 index 000000000..1e8d10e95 --- /dev/null +++ b/applications/main/subghz/protocols/power_smart.c @@ -0,0 +1,394 @@ +#include "power_smart.h" +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolPowerSmart" +#define POWER_SMART_PACKET_HEADER 0xFD000000AA000000 +#define POWER_SMART_PACKET_HEADER_MASK 0xFF000000FF000000 + +#define CHANNEL_PATTERN "%c%c%c%c%c%c" +#define CNT_TO_CHANNEL(dip) \ + (dip & 0x0001 ? '*' : '-'), (dip & 0x0002 ? '*' : '-'), (dip & 0x0004 ? '*' : '-'), \ + (dip & 0x0008 ? '*' : '-'), (dip & 0x0010 ? '*' : '-'), (dip & 0x0020 ? '*' : '-') + +static const SubGhzBlockConst subghz_protocol_power_smart_const = { + .te_short = 225, + .te_long = 450, + .te_delta = 100, + .min_count_bit_for_found = 64, +}; + +struct SubGhzProtocolDecoderPowerSmart { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + ManchesterState manchester_saved_state; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderPowerSmart { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + PowerSmartDecoderStepReset = 0, + PowerSmartDecoderFoundHeader, + PowerSmartDecoderStepDecoderData, +} PowerSmartDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_power_smart_decoder = { + .alloc = subghz_protocol_decoder_power_smart_alloc, + .free = subghz_protocol_decoder_power_smart_free, + + .feed = subghz_protocol_decoder_power_smart_feed, + .reset = subghz_protocol_decoder_power_smart_reset, + + .get_hash_data = subghz_protocol_decoder_power_smart_get_hash_data, + .serialize = subghz_protocol_decoder_power_smart_serialize, + .deserialize = subghz_protocol_decoder_power_smart_deserialize, + .get_string = subghz_protocol_decoder_power_smart_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_power_smart_encoder = { + .alloc = subghz_protocol_encoder_power_smart_alloc, + .free = subghz_protocol_encoder_power_smart_free, + + .deserialize = subghz_protocol_encoder_power_smart_deserialize, + .stop = subghz_protocol_encoder_power_smart_stop, + .yield = subghz_protocol_encoder_power_smart_yield, +}; + +const SubGhzProtocol subghz_protocol_power_smart = { + .name = SUBGHZ_PROTOCOL_POWER_SMART_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_power_smart_decoder, + .encoder = &subghz_protocol_power_smart_encoder, +}; + +void* subghz_protocol_encoder_power_smart_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderPowerSmart* instance = malloc(sizeof(SubGhzProtocolEncoderPowerSmart)); + + instance->base.protocol = &subghz_protocol_power_smart; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 1024; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_power_smart_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderPowerSmart* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static LevelDuration + subghz_protocol_encoder_power_smart_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_power_smart_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_power_smart_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_power_smart_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_power_smart_const.te_short; + data.level = true; + break; + + default: + furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderPowerSmart instance + */ +static void + subghz_protocol_encoder_power_smart_get_upload(SubGhzProtocolEncoderPowerSmart* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + for(int i = 8; i > 0; i--) { + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, !bit_read(instance->generic.data, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_power_smart_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, !bit_read(instance->generic.data, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_power_smart_add_duration_to_upload(result); + } + } + instance->encoder.upload[index] = subghz_protocol_encoder_power_smart_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_power_smart_const.te_long * 1111); + instance->encoder.size_upload = index; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_power_smart_remote_controller(SubGhzBlockGeneric* instance) { + /* + * Protocol: Manchester encoding, symbol rate ~2222. + * Packet Format: + * 0xFDXXXXYYAAZZZZWW where 0xFD and 0xAA sync word + * XXXX = ~ZZZZ, YY=(~WW)-1 + * Example: + * SYNC1 K1 CHANNEL DATA1 K2 DATA2 SYNC2 ~K1 ~CHANNEL ~DATA2 ~K2 (~DATA2)-1 + * 0xFD2137ACAADEC852 => 11111101 0 010000 10011011 1 10101100 10101010 1 1011110 1100100 0 01010010 + * 0xFDA137ACAA5EC852 => 11111101 1 010000 10011011 1 10101100 10101010 0 1011110 1100100 0 01010010 + * 0xFDA136ACAA5EC952 => 11111101 1 010000 10011011 0 10101100 10101010 0 1011110 1100100 1 01010010 + * + * Key: + * K1K2 + * 0 0 - key_unknown + * 0 1 - key_down + * 1 0 - key_up + * 1 1 - key_stop + * + */ + + instance->btn = ((instance->data >> 54) & 0x02) | ((instance->data >> 40) & 0x1); + instance->serial = ((instance->data >> 33) & 0x3FFF00) | ((instance->data >> 32) & 0xFF); + instance->cnt = ((instance->data >> 49) & 0x3F); +} + +bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderPowerSmart* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_power_smart_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_power_smart_remote_controller(&instance->generic); + subghz_protocol_encoder_power_smart_get_upload(instance); + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_power_smart_stop(void* context) { + SubGhzProtocolEncoderPowerSmart* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_power_smart_yield(void* context) { + SubGhzProtocolEncoderPowerSmart* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_power_smart_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderPowerSmart* instance = malloc(sizeof(SubGhzProtocolDecoderPowerSmart)); + instance->base.protocol = &subghz_protocol_power_smart; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_power_smart_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + free(instance); +} + +void subghz_protocol_decoder_power_smart_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +bool subghz_protocol_power_smart_chek_valid(uint64_t packet) { + uint32_t data_1 = (uint32_t)((packet >> 40) & 0xFFFF); + uint32_t data_2 = (uint32_t)((~packet >> 8) & 0xFFFF); + uint8_t data_3 = (uint8_t)(packet >> 32) & 0xFF; + uint8_t data_4 = (uint8_t)(((~packet) & 0xFF) - 1); + return (data_1 == data_2) && (data_3 == data_4); +} + +void subghz_protocol_decoder_power_smart_feed( + void* context, + bool level, + volatile uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + ManchesterEvent event = ManchesterEventReset; + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_short) < + subghz_protocol_power_smart_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_long) < + subghz_protocol_power_smart_const.te_delta * 2) { + event = ManchesterEventLongLow; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_short) < + subghz_protocol_power_smart_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_long) < + subghz_protocol_power_smart_const.te_delta * 2) { + event = ManchesterEventLongHigh; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; + } + if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) == + POWER_SMART_PACKET_HEADER) { + if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = + subghz_protocol_power_smart_const.min_count_bit_for_found; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + } + } else { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + } +} + +static const char* subghz_protocol_power_smart_get_name_button(uint8_t btn) { + btn &= 0x3; + const char* name_btn[0x4] = {"Unknown", "Down", "Up", "Stop"}; + return name_btn[btn]; +} + +uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_power_smart_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_power_smart_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_power_smart_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + subghz_protocol_power_smart_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%07lX \r\n" + "Btn:%s\r\n" + "Channel:" CHANNEL_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + subghz_protocol_power_smart_get_name_button(instance->generic.btn), + CNT_TO_CHANNEL(instance->generic.cnt)); +} diff --git a/applications/main/subghz/protocols/power_smart.h b/applications/main/subghz/protocols/power_smart.h new file mode 100644 index 000000000..806729f8e --- /dev/null +++ b/applications/main/subghz/protocols/power_smart.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_POWER_SMART_NAME "Power Smart" + +typedef struct SubGhzProtocolDecoderPowerSmart SubGhzProtocolDecoderPowerSmart; +typedef struct SubGhzProtocolEncoderPowerSmart SubGhzProtocolEncoderPowerSmart; + +extern const SubGhzProtocolDecoder subghz_protocol_power_smart_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_power_smart_encoder; +extern const SubGhzProtocol subghz_protocol_power_smart; + +/** + * Allocate SubGhzProtocolEncoderPowerSmart. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderPowerSmart* pointer to a SubGhzProtocolEncoderPowerSmart instance + */ +void* subghz_protocol_encoder_power_smart_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderPowerSmart. + * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance + */ +void subghz_protocol_encoder_power_smart_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance + */ +void subghz_protocol_encoder_power_smart_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_power_smart_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderPowerSmart. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderPowerSmart* pointer to a SubGhzProtocolDecoderPowerSmart instance + */ +void* subghz_protocol_decoder_power_smart_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderPowerSmart. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + */ +void subghz_protocol_decoder_power_smart_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderPowerSmart. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + */ +void subghz_protocol_decoder_power_smart_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_power_smart_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderPowerSmart. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_power_smart_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderPowerSmart. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + * @param output Resulting text + */ +void subghz_protocol_decoder_power_smart_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/princeton.c b/applications/main/subghz/protocols/princeton.c new file mode 100644 index 000000000..7fc8f6524 --- /dev/null +++ b/applications/main/subghz/protocols/princeton.c @@ -0,0 +1,374 @@ +#include "princeton.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://phreakerclub.com/447 + * + */ + +#define TAG "SubGhzProtocolPrinceton" + +static const SubGhzBlockConst subghz_protocol_princeton_const = { + .te_short = 390, + .te_long = 1170, + .te_delta = 300, + .min_count_bit_for_found = 24, +}; + +struct SubGhzProtocolDecoderPrinceton { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t te; + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderPrinceton { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint32_t te; +}; + +typedef enum { + PrincetonDecoderStepReset = 0, + PrincetonDecoderStepSaveDuration, + PrincetonDecoderStepCheckDuration, +} PrincetonDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_princeton_decoder = { + .alloc = subghz_protocol_decoder_princeton_alloc, + .free = subghz_protocol_decoder_princeton_free, + + .feed = subghz_protocol_decoder_princeton_feed, + .reset = subghz_protocol_decoder_princeton_reset, + + .get_hash_data = subghz_protocol_decoder_princeton_get_hash_data, + .serialize = subghz_protocol_decoder_princeton_serialize, + .deserialize = subghz_protocol_decoder_princeton_deserialize, + .get_string = subghz_protocol_decoder_princeton_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_princeton_encoder = { + .alloc = subghz_protocol_encoder_princeton_alloc, + .free = subghz_protocol_encoder_princeton_free, + + .deserialize = subghz_protocol_encoder_princeton_deserialize, + .stop = subghz_protocol_encoder_princeton_stop, + .yield = subghz_protocol_encoder_princeton_yield, +}; + +const SubGhzProtocol subghz_protocol_princeton = { + .name = SUBGHZ_PROTOCOL_PRINCETON_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_princeton_decoder, + .encoder = &subghz_protocol_princeton_encoder, +}; + +void* subghz_protocol_encoder_princeton_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderPrinceton* instance = malloc(sizeof(SubGhzProtocolEncoderPrinceton)); + + instance->base.protocol = &subghz_protocol_princeton; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop) + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_princeton_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderPrinceton* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderPrinceton instance + * @return true On success + */ +static bool + subghz_protocol_encoder_princeton_get_upload(SubGhzProtocolEncoderPrinceton* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)instance->te * 3); + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)instance->te * 3); + } + } + + //Send Stop bit + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + //Send PT_GUARD + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 30); + + return true; +} + +bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderPrinceton* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_princeton_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_princeton_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_princeton_stop(void* context) { + SubGhzProtocolEncoderPrinceton* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_princeton_yield(void* context) { + SubGhzProtocolEncoderPrinceton* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_princeton_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderPrinceton* instance = malloc(sizeof(SubGhzProtocolDecoderPrinceton)); + instance->base.protocol = &subghz_protocol_princeton; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_princeton_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + free(instance); +} + +void subghz_protocol_decoder_princeton_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + instance->decoder.parser_step = PrincetonDecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + + switch(instance->decoder.parser_step) { + case PrincetonDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_short * 36) < + subghz_protocol_princeton_const.te_delta * 36)) { + //Found Preambula + instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + } + break; + case PrincetonDecoderStepSaveDuration: + //save duration + if(level) { + instance->decoder.te_last = duration; + instance->te += duration; + instance->decoder.parser_step = PrincetonDecoderStepCheckDuration; + } + break; + case PrincetonDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_long * 2)) { + instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; + if(instance->decoder.decode_count_bit == + subghz_protocol_princeton_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->te /= (instance->decoder.decode_count_bit * 4 + 1); + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->last_data = instance->decoder.decode_data; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + break; + } + + instance->te += duration; + + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_princeton_const.te_short) < + subghz_protocol_princeton_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_long) < + subghz_protocol_princeton_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_princeton_const.te_long) < + subghz_protocol_princeton_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_short) < + subghz_protocol_princeton_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = PrincetonDecoderStepReset; + } + } else { + instance->decoder.parser_step = PrincetonDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_princeton_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->serial = instance->data >> 4; + instance->btn = instance->data & 0xF; +} + +uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_princeton_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_princeton_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + subghz_protocol_princeton_check_remote_controller(&instance->generic); + uint32_t data_rev = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Yek:0x%08lX\r\n" + "Sn:0x%05lX Btn:%01X\r\n" + "Te:%luus\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFF), + data_rev, + instance->generic.serial, + instance->generic.btn, + instance->te); +} diff --git a/applications/main/subghz/protocols/princeton.h b/applications/main/subghz/protocols/princeton.h new file mode 100644 index 000000000..a2a11292e --- /dev/null +++ b/applications/main/subghz/protocols/princeton.h @@ -0,0 +1,115 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_PRINCETON_NAME "Princeton" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzProtocolDecoderPrinceton SubGhzProtocolDecoderPrinceton; +typedef struct SubGhzProtocolEncoderPrinceton SubGhzProtocolEncoderPrinceton; + +extern const SubGhzProtocolDecoder subghz_protocol_princeton_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_princeton_encoder; +extern const SubGhzProtocol subghz_protocol_princeton; + +/** + * Allocate SubGhzProtocolEncoderPrinceton. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderPrinceton* pointer to a SubGhzProtocolEncoderPrinceton instance + */ +void* subghz_protocol_encoder_princeton_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderPrinceton. + * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance + */ +void subghz_protocol_encoder_princeton_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance + */ +void subghz_protocol_encoder_princeton_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_princeton_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderPrinceton. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderPrinceton* pointer to a SubGhzProtocolDecoderPrinceton instance + */ +void* subghz_protocol_decoder_princeton_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderPrinceton. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + */ +void subghz_protocol_decoder_princeton_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderPrinceton. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + */ +void subghz_protocol_decoder_princeton_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderPrinceton. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_princeton_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderPrinceton. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + * @param output Resulting text + */ +void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/protocols/princeton_for_testing.c b/applications/main/subghz/protocols/princeton_for_testing.c new file mode 100644 index 000000000..478d14cdf --- /dev/null +++ b/applications/main/subghz/protocols/princeton_for_testing.c @@ -0,0 +1,288 @@ +#include "princeton_for_testing.h" + +#include +#include "../blocks/math.h" + +/* + * Help + * https://phreakerclub.com/447 + * + */ + +#define SUBGHZ_PT_SHORT 300 +#define SUBGHZ_PT_LONG (SUBGHZ_PT_SHORT * 3) +#define SUBGHZ_PT_GUARD (SUBGHZ_PT_SHORT * 30) +#define SUBGHZ_PT_COUNT_KEY_433 9 +#define SUBGHZ_PT_TIMEOUT_433 900 +#define SUBGHZ_PT_COUNT_KEY_868 9 +#define SUBGHZ_PT_TIMEOUT_868 14000 + +#define TAG "SubGhzProtocolPrinceton" + +struct SubGhzEncoderPrinceton { + uint32_t key; + uint16_t te; + size_t repeat; + size_t front; + size_t count_key; + size_t count_key_package; + uint32_t time_high; + uint32_t time_low; + uint32_t timeout; + uint32_t time_stop; +}; + +typedef enum { + PrincetonDecoderStepReset = 0, + PrincetonDecoderStepSaveDuration, + PrincetonDecoderStepCheckDuration, +} PrincetonDecoderStep; + +SubGhzEncoderPrinceton* subghz_encoder_princeton_for_testing_alloc() { + SubGhzEncoderPrinceton* instance = malloc(sizeof(SubGhzEncoderPrinceton)); + return instance; +} + +void subghz_encoder_princeton_for_testing_free(SubGhzEncoderPrinceton* instance) { + furi_assert(instance); + free(instance); +} + +void subghz_encoder_princeton_for_testing_stop( + SubGhzEncoderPrinceton* instance, + uint32_t time_stop) { + instance->time_stop = time_stop; +} + +void subghz_encoder_princeton_for_testing_set( + SubGhzEncoderPrinceton* instance, + uint32_t key, + size_t repeat, + uint32_t frequency) { + furi_assert(instance); + instance->te = SUBGHZ_PT_SHORT; + instance->key = key; + instance->repeat = repeat + 1; + instance->front = 48; + instance->time_high = 0; + instance->time_low = 0; + if(frequency < 700000000) { + instance->count_key_package = SUBGHZ_PT_COUNT_KEY_433; + instance->timeout = SUBGHZ_PT_TIMEOUT_433; + } else { + instance->count_key_package = SUBGHZ_PT_COUNT_KEY_868; + instance->timeout = SUBGHZ_PT_TIMEOUT_868; + } + + instance->count_key = instance->count_key_package + 3; + + if((furi_get_tick() - instance->time_stop) < instance->timeout) { + instance->time_stop = (instance->timeout - (furi_get_tick() - instance->time_stop)) * 1000; + } else { + instance->time_stop = 0; + } +} + +size_t subghz_encoder_princeton_for_testing_get_repeat_left(SubGhzEncoderPrinceton* instance) { + furi_assert(instance); + return instance->repeat; +} + +void subghz_encoder_princeton_for_testing_print_log(void* context) { + SubGhzEncoderPrinceton* instance = context; + float duty_cycle = + ((float)instance->time_high / (instance->time_high + instance->time_low)) * 100; + FURI_LOG_I( + TAG "Encoder", + "Radio tx_time=%luus ON=%luus, OFF=%luus, DutyCycle=%lu,%lu%%", + instance->time_high + instance->time_low, + instance->time_high, + instance->time_low, + (uint32_t)duty_cycle, + (uint32_t)((duty_cycle - (uint32_t)duty_cycle) * 100UL)); +} + +LevelDuration subghz_encoder_princeton_for_testing_yield(void* context) { + SubGhzEncoderPrinceton* instance = context; + if(instance->repeat == 0) { + subghz_encoder_princeton_for_testing_print_log(instance); + return level_duration_reset(); + } + + size_t bit = instance->front / 2; + bool level = !(instance->front % 2); + + LevelDuration ret; + if(bit < 24) { + uint8_t byte = bit / 8; + uint8_t bit_in_byte = bit % 8; + bool value = (((uint8_t*)&instance->key)[2 - byte] >> (7 - bit_in_byte)) & 1; + if(value) { + ret = level_duration_make(level, level ? instance->te * 3 : instance->te); + if(level) + instance->time_high += instance->te * 3; + else + instance->time_low += instance->te; + } else { + ret = level_duration_make(level, level ? instance->te : instance->te * 3); + if(level) + instance->time_high += instance->te; + else + instance->time_low += instance->te * 3; + } + } else { + if(instance->time_stop) { + ret = level_duration_make(level, level ? instance->te : instance->time_stop); + if(level) + instance->time_high += instance->te; + else { + instance->time_low += instance->time_stop; + instance->time_stop = 0; + instance->front = 47; + } + } else { + if(--instance->count_key != 0) { + ret = level_duration_make(level, level ? instance->te : instance->te * 30); + if(level) + instance->time_high += instance->te; + else + instance->time_low += instance->te * 30; + } else { + instance->count_key = instance->count_key_package + 2; + instance->front = 48; + ret = level_duration_make(level, level ? instance->te : instance->timeout * 1000); + if(level) + instance->time_high += instance->te; + else + instance->time_low += instance->timeout * 1000; + } + } + } + + instance->front++; + if(instance->front == 50) { + instance->repeat--; + instance->front = 0; + } + return ret; +} + +struct SubGhzDecoderPrinceton { + const char* name; + uint16_t te_long; + uint16_t te_short; + uint16_t te_delta; + uint8_t code_count_bit; + uint8_t code_last_count_bit; + uint64_t code_found; + uint64_t code_last_found; + uint8_t code_min_count_bit_for_found; + uint8_t btn; + uint32_t te_last; + uint32_t serial; + uint32_t parser_step; + uint16_t cnt; + uint32_t te; + + SubGhzDecoderPrincetonCallback callback; + void* context; +}; + +SubGhzDecoderPrinceton* subghz_decoder_princeton_for_testing_alloc(void) { + SubGhzDecoderPrinceton* instance = malloc(sizeof(SubGhzDecoderPrinceton)); + + instance->te = SUBGHZ_PT_SHORT; + instance->name = "Princeton"; + instance->code_min_count_bit_for_found = 24; + instance->te_short = 400; + instance->te_long = 1200; + instance->te_delta = 250; + return instance; +} + +void subghz_decoder_princeton_for_testing_free(SubGhzDecoderPrinceton* instance) { + furi_assert(instance); + free(instance); +} + +void subghz_decoder_princeton_for_testing_set_callback( + SubGhzDecoderPrinceton* instance, + SubGhzDecoderPrincetonCallback callback, + void* context) { + instance->callback = callback; + instance->context = context; +} + +void subghz_decoder_princeton_for_testing_reset(SubGhzDecoderPrinceton* instance) { + instance->parser_step = PrincetonDecoderStepReset; +} + +static void + subghz_decoder_princeton_for_testing_add_bit(SubGhzDecoderPrinceton* instance, uint8_t bit) { + instance->code_found = instance->code_found << 1 | bit; + instance->code_count_bit++; +} + +void subghz_decoder_princeton_for_testing_parse( + SubGhzDecoderPrinceton* instance, + bool level, + uint32_t duration) { + switch(instance->parser_step) { + case PrincetonDecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, instance->te_short * 36) < instance->te_delta * 36)) { + //Found Preambula + instance->parser_step = PrincetonDecoderStepSaveDuration; + instance->code_found = 0; + instance->code_count_bit = 0; + instance->te = 0; + } + break; + case PrincetonDecoderStepSaveDuration: + //save duration + if(level) { + instance->te_last = duration; + instance->te += duration; + instance->parser_step = PrincetonDecoderStepCheckDuration; + } + break; + case PrincetonDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)instance->te_short * 10 + instance->te_delta)) { + instance->parser_step = PrincetonDecoderStepSaveDuration; + if(instance->code_count_bit == instance->code_min_count_bit_for_found) { + instance->te /= (instance->code_count_bit * 4 + 1); + + instance->code_last_found = instance->code_found; + instance->code_last_count_bit = instance->code_count_bit; + instance->serial = instance->code_found >> 4; + instance->btn = (uint8_t)instance->code_found & 0x00000F; + + if(instance->callback) instance->callback(instance, instance->context); + } + instance->code_found = 0; + instance->code_count_bit = 0; + instance->te = 0; + break; + } + + instance->te += duration; + + if((DURATION_DIFF(instance->te_last, instance->te_short) < instance->te_delta) && + (DURATION_DIFF(duration, instance->te_long) < instance->te_delta * 3)) { + subghz_decoder_princeton_for_testing_add_bit(instance, 0); + instance->parser_step = PrincetonDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->te_last, instance->te_long) < instance->te_delta * 3) && + (DURATION_DIFF(duration, instance->te_short) < instance->te_delta)) { + subghz_decoder_princeton_for_testing_add_bit(instance, 1); + instance->parser_step = PrincetonDecoderStepSaveDuration; + } else { + instance->parser_step = PrincetonDecoderStepReset; + } + } else { + instance->parser_step = PrincetonDecoderStepReset; + } + break; + } +} diff --git a/applications/main/subghz/protocols/princeton_for_testing.h b/applications/main/subghz/protocols/princeton_for_testing.h new file mode 100644 index 000000000..07a37ec5f --- /dev/null +++ b/applications/main/subghz/protocols/princeton_for_testing.h @@ -0,0 +1,97 @@ +#pragma once + +#include "base.h" + +/** SubGhzDecoderPrinceton anonymous type */ +typedef struct SubGhzDecoderPrinceton SubGhzDecoderPrinceton; +/** SubGhzEncoderPrinceton anonymous type */ +typedef struct SubGhzEncoderPrinceton SubGhzEncoderPrinceton; + +typedef void (*SubGhzDecoderPrincetonCallback)(SubGhzDecoderPrinceton* parser, void* context); + +/** + * Allocate SubGhzEncoderPrinceton + * @return pointer to SubGhzEncoderPrinceton instance + */ +SubGhzEncoderPrinceton* subghz_encoder_princeton_for_testing_alloc(); + +/** + * Free SubGhzEncoderPrinceton instance + * @param instance - SubGhzEncoderPrinceton instance + */ +void subghz_encoder_princeton_for_testing_free(SubGhzEncoderPrinceton* instance); + +/** + * Forced transmission stop. + * @param instance Pointer to a SubGhzEncoderPrinceton instance + * @param time_stop Transmission stop time, ms + */ +void subghz_encoder_princeton_for_testing_stop( + SubGhzEncoderPrinceton* instance, + uint32_t time_stop); + +/** + * Set new encoder params + * @param instance - SubGhzEncoderPrinceton instance + * @param key - 24bit key + * @param repeat - how many times to repeat + * @param frequency - frequency + */ +void subghz_encoder_princeton_for_testing_set( + SubGhzEncoderPrinceton* instance, + uint32_t key, + size_t repeat, + uint32_t frequency); + +/** + * Get repeat count left + * @param instance - SubGhzEncoderPrinceton instance + * @return repeat count left + */ +size_t subghz_encoder_princeton_for_testing_get_repeat_left(SubGhzEncoderPrinceton* instance); + +/** + * Print encoder log + * @param instance - SubGhzEncoderPrinceton instance + */ +void subghz_encoder_princeton_for_testing_print_log(void* context); + +/** + * Get level duration + * @param instance - SubGhzEncoderPrinceton instance + * @return level duration + */ +LevelDuration subghz_encoder_princeton_for_testing_yield(void* context); + +/** + * Allocate SubGhzDecoderPrinceton + * @return SubGhzDecoderPrinceton* + */ +SubGhzDecoderPrinceton* subghz_decoder_princeton_for_testing_alloc(); + +/** + * Free SubGhzDecoderPrinceton + * @param instance + */ +void subghz_decoder_princeton_for_testing_free(SubGhzDecoderPrinceton* instance); + +void subghz_decoder_princeton_for_testing_set_callback( + SubGhzDecoderPrinceton* instance, + SubGhzDecoderPrincetonCallback callback, + void* context); + +/** + * Reset internal state + * @param instance - SubGhzDecoderPrinceton instance + */ +void subghz_decoder_princeton_for_testing_reset(SubGhzDecoderPrinceton* instance); + +/** + * Parse accepted duration + * @param instance - SubGhzDecoderPrinceton instance + * @param data - LevelDuration level_duration + */ +void subghz_decoder_princeton_for_testing_parse( + SubGhzDecoderPrinceton* instance, + bool level, + uint32_t duration); diff --git a/applications/main/subghz/protocols/protocol_items.c b/applications/main/subghz/protocols/protocol_items.c new file mode 100644 index 000000000..74244c5ff --- /dev/null +++ b/applications/main/subghz/protocols/protocol_items.c @@ -0,0 +1,50 @@ +#include "protocol_items.h" + +const SubGhzProtocol* subghz_protocol_registry_items[] = { + &subghz_protocol_gate_tx, + &subghz_protocol_keeloq, + &subghz_protocol_star_line, + &subghz_protocol_nice_flo, + &subghz_protocol_came, + &subghz_protocol_faac_slh, + &subghz_protocol_nice_flor_s, + &subghz_protocol_came_twee, + &subghz_protocol_came_atomo, + &subghz_protocol_nero_sketch, + &subghz_protocol_ido, + &subghz_protocol_kia, + &subghz_protocol_hormann, + &subghz_protocol_nero_radio, + &subghz_protocol_somfy_telis, + &subghz_protocol_somfy_keytis, + &subghz_protocol_scher_khan, + &subghz_protocol_princeton, + &subghz_protocol_raw, + &subghz_protocol_linear, + &subghz_protocol_secplus_v2, + &subghz_protocol_secplus_v1, + &subghz_protocol_megacode, + &subghz_protocol_holtek, + &subghz_protocol_chamb_code, + &subghz_protocol_power_smart, + &subghz_protocol_marantec, + &subghz_protocol_bett, + &subghz_protocol_doitrand, + &subghz_protocol_phoenix_v2, + &subghz_protocol_honeywell_wdb, + &subghz_protocol_magellan, + &subghz_protocol_intertechno_v3, + &subghz_protocol_clemsa, + &subghz_protocol_ansonic, + &subghz_protocol_smc5326, + &subghz_protocol_holtek_th12x, + &subghz_protocol_linear_delta3, + &subghz_protocol_dooya, + &subghz_protocol_alutech_at_4n, + &subghz_protocol_kinggates_stylo_4k, + &subghz_protocol_bin_raw, +}; + +const SubGhzProtocolRegistry subghz_protocol_registry = { + .items = subghz_protocol_registry_items, + .size = COUNT_OF(subghz_protocol_registry_items)}; diff --git a/applications/main/subghz/protocols/protocol_items.h b/applications/main/subghz/protocols/protocol_items.h new file mode 100644 index 000000000..b7e082bf5 --- /dev/null +++ b/applications/main/subghz/protocols/protocol_items.h @@ -0,0 +1,55 @@ +#pragma once +#include "../registry.h" + +#include "princeton.h" +#include "keeloq.h" +#include "star_line.h" +#include "nice_flo.h" +#include "came.h" +#include "faac_slh.h" +#include "nice_flor_s.h" +#include "came_twee.h" +#include "came_atomo.h" +#include "nero_sketch.h" +#include "ido.h" +#include "kia.h" +#include "hormann.h" +#include "nero_radio.h" +#include "somfy_telis.h" +#include "somfy_keytis.h" +#include "scher_khan.h" +#include "gate_tx.h" +#include "raw.h" +#include "linear.h" +#include "linear_delta3.h" +#include "secplus_v2.h" +#include "secplus_v1.h" +#include "megacode.h" +#include "holtek.h" +#include "chamberlain_code.h" +#include "power_smart.h" +#include "marantec.h" +#include "bett.h" +#include "doitrand.h" +#include "phoenix_v2.h" +#include "honeywell_wdb.h" +#include "magellan.h" +#include "intertechno_v3.h" +#include "clemsa.h" +#include "ansonic.h" +#include "smc5326.h" +#include "holtek_ht12x.h" +#include "dooya.h" +#include "alutech_at_4n.h" +#include "kinggates_stylo_4k.h" +#include "bin_raw.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const SubGhzProtocolRegistry subghz_protocol_registry; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/subghz/protocols/raw.c b/applications/main/subghz/protocols/raw.c new file mode 100644 index 000000000..01a229047 --- /dev/null +++ b/applications/main/subghz/protocols/raw.c @@ -0,0 +1,374 @@ +#include "raw.h" +#include +#include "../subghz_file_encoder_worker.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#include +#include + +#define TAG "SubGhzProtocolRAW" +#define SUBGHZ_DOWNLOAD_MAX_SIZE 512 + +static const SubGhzBlockConst subghz_protocol_raw_const = { + .te_short = 50, + .te_long = 32700, + .te_delta = 0, + .min_count_bit_for_found = 0, +}; + +struct SubGhzProtocolDecoderRAW { + SubGhzProtocolDecoderBase base; + + int32_t* upload_raw; + uint16_t ind_write; + Storage* storage; + FlipperFormat* flipper_file; + uint32_t file_is_open; + FuriString* file_name; + size_t sample_write; + bool last_level; + bool pause; +}; + +struct SubGhzProtocolEncoderRAW { + SubGhzProtocolEncoderBase base; + + bool is_running; + FuriString* file_name; + SubGhzFileEncoderWorker* file_worker_encoder; +}; + +typedef enum { + RAWFileIsOpenClose = 0, + RAWFileIsOpenWrite, + RAWFileIsOpenRead, +} RAWFilIsOpen; + +const SubGhzProtocolDecoder subghz_protocol_raw_decoder = { + .alloc = subghz_protocol_decoder_raw_alloc, + .free = subghz_protocol_decoder_raw_free, + + .feed = subghz_protocol_decoder_raw_feed, + .reset = subghz_protocol_decoder_raw_reset, + + .get_hash_data = NULL, + .serialize = NULL, + .deserialize = subghz_protocol_decoder_raw_deserialize, + .get_string = subghz_protocol_decoder_raw_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_raw_encoder = { + .alloc = subghz_protocol_encoder_raw_alloc, + .free = subghz_protocol_encoder_raw_free, + + .deserialize = subghz_protocol_encoder_raw_deserialize, + .stop = subghz_protocol_encoder_raw_stop, + .yield = subghz_protocol_encoder_raw_yield, +}; + +const SubGhzProtocol subghz_protocol_raw = { + .name = SUBGHZ_PROTOCOL_RAW_NAME, + .type = SubGhzProtocolTypeRAW, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_RAW | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_raw_decoder, + .encoder = &subghz_protocol_raw_encoder, +}; + +bool subghz_protocol_raw_save_to_file_init( + SubGhzProtocolDecoderRAW* instance, + const char* dev_name, + SubGhzRadioPreset* preset) { + furi_assert(instance); + + instance->storage = furi_record_open(RECORD_STORAGE); + instance->flipper_file = flipper_format_file_alloc(instance->storage); + + FuriString* temp_str; + temp_str = furi_string_alloc(); + bool init = false; + + do { + // Create subghz folder directory if necessary + if(!storage_simply_mkdir(instance->storage, SUBGHZ_RAW_FOLDER)) { + break; + } + // Create saved directory if necessary + if(!storage_simply_mkdir(instance->storage, SUBGHZ_RAW_FOLDER)) { + break; + } + + furi_string_set(instance->file_name, dev_name); + // First remove subghz device file if it was saved + furi_string_printf(temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, dev_name, SUBGHZ_APP_EXTENSION); + + if(!storage_simply_remove(instance->storage, furi_string_get_cstr(temp_str))) { + break; + } + + // Open file + if(!flipper_format_file_open_always( + instance->flipper_file, furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to open file for write: %s", furi_string_get_cstr(temp_str)); + break; + } + + if(!flipper_format_write_header_cstr( + instance->flipper_file, SUBGHZ_RAW_FILE_TYPE, SUBGHZ_RAW_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32( + instance->flipper_file, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + instance->flipper_file, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + instance->flipper_file, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + instance->flipper_file, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr( + instance->flipper_file, "Protocol", instance->base.protocol->name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + instance->upload_raw = malloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(int32_t)); + instance->file_is_open = RAWFileIsOpenWrite; + instance->sample_write = 0; + instance->last_level = false; + instance->pause = false; + init = true; + } while(0); + + furi_string_free(temp_str); + + return init; +} + +static bool subghz_protocol_raw_save_to_file_write(SubGhzProtocolDecoderRAW* instance) { + furi_assert(instance); + + bool is_write = false; + if(instance->file_is_open == RAWFileIsOpenWrite) { + if(!flipper_format_write_int32( + instance->flipper_file, "RAW_Data", instance->upload_raw, instance->ind_write)) { + FURI_LOG_E(TAG, "Unable to add RAW_Data"); + } else { + instance->sample_write += instance->ind_write; + instance->ind_write = 0; + is_write = true; + } + } + return is_write; +} + +void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance) { + furi_assert(instance); + + if(instance->file_is_open == RAWFileIsOpenWrite && instance->ind_write) + subghz_protocol_raw_save_to_file_write(instance); + if(instance->file_is_open != RAWFileIsOpenClose) { + free(instance->upload_raw); + instance->upload_raw = NULL; + flipper_format_file_close(instance->flipper_file); + flipper_format_free(instance->flipper_file); + furi_record_close(RECORD_STORAGE); + } + + instance->file_is_open = RAWFileIsOpenClose; +} + +void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause) { + furi_assert(instance); + + if(instance->pause != pause) { + instance->pause = pause; + } +} + +size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance) { + return instance->sample_write + instance->ind_write; +} + +void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderRAW* instance = malloc(sizeof(SubGhzProtocolDecoderRAW)); + instance->base.protocol = &subghz_protocol_raw; + instance->upload_raw = NULL; + instance->ind_write = 0; + instance->last_level = false; + instance->file_is_open = RAWFileIsOpenClose; + instance->file_name = furi_string_alloc(); + + return instance; +} + +void subghz_protocol_decoder_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderRAW* instance = context; + furi_string_free(instance->file_name); + free(instance); +} + +void subghz_protocol_decoder_raw_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderRAW* instance = context; + instance->ind_write = 0; + instance->last_level = false; +} + +void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderRAW* instance = context; + + if(!instance->pause && (instance->upload_raw != NULL)) { + if(duration > subghz_protocol_raw_const.te_short) { + if(instance->last_level != level) { + instance->last_level = (level ? true : false); + instance->upload_raw[instance->ind_write++] = (level ? duration : -duration); + } + } + + if(instance->ind_write == SUBGHZ_DOWNLOAD_MAX_SIZE) { + subghz_protocol_raw_save_to_file_write(instance); + } + } +} + +bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + UNUSED(context); + UNUSED(flipper_format); + //ToDo stub, for backwards compatibility + return true; +} + +void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output) { + furi_assert(context); + //SubGhzProtocolDecoderRAW* instance = context; + UNUSED(context); + //ToDo no use + furi_string_cat_printf(output, "RAW Data"); +} + +void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderRAW* instance = malloc(sizeof(SubGhzProtocolEncoderRAW)); + + instance->base.protocol = &subghz_protocol_raw; + instance->file_name = furi_string_alloc(); + instance->is_running = false; + return instance; +} + +void subghz_protocol_encoder_raw_stop(void* context) { + SubGhzProtocolEncoderRAW* instance = context; + instance->is_running = false; + if(subghz_file_encoder_worker_is_running(instance->file_worker_encoder)) { + subghz_file_encoder_worker_stop(instance->file_worker_encoder); + subghz_file_encoder_worker_free(instance->file_worker_encoder); + } +} + +void subghz_protocol_encoder_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderRAW* instance = context; + subghz_protocol_encoder_raw_stop(instance); + furi_string_free(instance->file_name); + free(instance); +} + +void subghz_protocol_raw_file_encoder_worker_set_callback_end( + SubGhzProtocolEncoderRAW* instance, + SubGhzProtocolEncoderRAWCallbackEnd callback_end, + void* context_end) { + furi_assert(instance); + furi_assert(callback_end); + subghz_file_encoder_worker_callback_end( + instance->file_worker_encoder, callback_end, context_end); +} + +static bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* instance) { + furi_assert(instance); + + instance->file_worker_encoder = subghz_file_encoder_worker_alloc(); + if(subghz_file_encoder_worker_start( + instance->file_worker_encoder, furi_string_get_cstr(instance->file_name))) { + //the worker needs a file in order to open and read part of the file + furi_delay_ms(100); + instance->is_running = true; + } else { + subghz_protocol_encoder_raw_stop(instance); + } + return instance->is_running; +} + +void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path) { + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_string_cstr(flipper_format, "Protocol", "RAW")) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + if(!flipper_format_write_string_cstr(flipper_format, "File_name", file_path)) { + FURI_LOG_E(TAG, "Unable to add File_name"); + break; + } + } while(false); +} + +bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderRAW* instance = context; + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + if(!flipper_format_read_string(flipper_format, "File_name", temp_str)) { + FURI_LOG_E(TAG, "Missing File_name"); + break; + } + furi_string_set(instance->file_name, temp_str); + + res = subghz_protocol_encoder_raw_worker_init(instance); + } while(false); + furi_string_free(temp_str); + return res; +} + +LevelDuration subghz_protocol_encoder_raw_yield(void* context) { + SubGhzProtocolEncoderRAW* instance = context; + + if(!instance->is_running) return level_duration_reset(); + return subghz_file_encoder_worker_get_level_duration(instance->file_worker_encoder); +} diff --git a/applications/main/subghz/protocols/raw.h b/applications/main/subghz/protocols/raw.h new file mode 100644 index 000000000..44c7faec5 --- /dev/null +++ b/applications/main/subghz/protocols/raw.h @@ -0,0 +1,148 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_RAW_NAME "RAW" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*SubGhzProtocolEncoderRAWCallbackEnd)(void* context); + +typedef struct SubGhzProtocolDecoderRAW SubGhzProtocolDecoderRAW; +typedef struct SubGhzProtocolEncoderRAW SubGhzProtocolEncoderRAW; + +extern const SubGhzProtocolDecoder subghz_protocol_raw_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_raw_encoder; +extern const SubGhzProtocol subghz_protocol_raw; + +/** + * Open file for writing + * @param instance Pointer to a SubGhzProtocolDecoderRAW instance + * @param dev_name File name + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_raw_save_to_file_init( + SubGhzProtocolDecoderRAW* instance, + const char* dev_name, + SubGhzRadioPreset* preset); + +/** + * Stop writing file to flash + * @param instance Pointer to a SubGhzProtocolDecoderRAW instance + */ +void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance); + +/** + * Get the number of samples received SubGhzProtocolDecoderRAW. + * @param instance Pointer to a SubGhzProtocolDecoderRAW instance + * @return count of samples + */ +size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance); + +/** + * Allocate SubGhzProtocolDecoderRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderRAW* pointer to a SubGhzProtocolDecoderRAW instance + */ +void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderRAW. + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + */ +void subghz_protocol_decoder_raw_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderRAW. + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + */ +void subghz_protocol_decoder_raw_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration); + +/** + * Deserialize data SubGhzProtocolDecoderRAW. + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + * @param output Resulting text + */ +void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output); + +/** + * Allocate SubGhzProtocolEncoderRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderRAW* pointer to a SubGhzProtocolEncoderRAW instance + */ +void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderRAW. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + */ +void subghz_protocol_encoder_raw_free(void* context); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + */ +void subghz_protocol_encoder_raw_stop(void* context); + +/** + * pause writing to flash. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + * @param pause pause writing + */ +void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause); + +/** + * Set callback on completion of file transfer. + * @param instance Pointer to a SubGhzProtocolEncoderRAW instance + * @param callback_end Callback, SubGhzProtocolEncoderRAWCallbackEnd + * @param context_end Context + */ +void subghz_protocol_raw_file_encoder_worker_set_callback_end( + SubGhzProtocolEncoderRAW* instance, + SubGhzProtocolEncoderRAWCallbackEnd callback_end, + void* context_end); + +/** + * File generation for RAW work. + * @param flipper_format Pointer to a FlipperFormat instance + * @param file_path File path + */ +void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_raw_yield(void* context); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/protocols/scher_khan.c b/applications/main/subghz/protocols/scher_khan.c new file mode 100644 index 000000000..a5de7d04b --- /dev/null +++ b/applications/main/subghz/protocols/scher_khan.c @@ -0,0 +1,288 @@ +#include "scher_khan.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +//https://phreakerclub.com/72 +//https://phreakerclub.com/forum/showthread.php?t=7&page=2 +//https://phreakerclub.com/forum/showthread.php?t=274&highlight=magicar +//!!! https://phreakerclub.com/forum/showthread.php?t=489&highlight=magicar&page=5 + +#define TAG "SubGhzProtocolScherKhan" + +static const SubGhzBlockConst subghz_protocol_scher_khan_const = { + .te_short = 750, + .te_long = 1100, + .te_delta = 150, + .min_count_bit_for_found = 35, +}; + +struct SubGhzProtocolDecoderScherKhan { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + const char* protocol_name; +}; + +struct SubGhzProtocolEncoderScherKhan { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + ScherKhanDecoderStepReset = 0, + ScherKhanDecoderStepCheckPreambula, + ScherKhanDecoderStepSaveDuration, + ScherKhanDecoderStepCheckDuration, +} ScherKhanDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder = { + .alloc = subghz_protocol_decoder_scher_khan_alloc, + .free = subghz_protocol_decoder_scher_khan_free, + + .feed = subghz_protocol_decoder_scher_khan_feed, + .reset = subghz_protocol_decoder_scher_khan_reset, + + .get_hash_data = subghz_protocol_decoder_scher_khan_get_hash_data, + .serialize = subghz_protocol_decoder_scher_khan_serialize, + .deserialize = subghz_protocol_decoder_scher_khan_deserialize, + .get_string = subghz_protocol_decoder_scher_khan_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_scher_khan = { + .name = SUBGHZ_PROTOCOL_SCHER_KHAN_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Save, + + .decoder = &subghz_protocol_scher_khan_decoder, + .encoder = &subghz_protocol_scher_khan_encoder, +}; + +void* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderScherKhan* instance = malloc(sizeof(SubGhzProtocolDecoderScherKhan)); + instance->base.protocol = &subghz_protocol_scher_khan; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_scher_khan_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + free(instance); +} + +void subghz_protocol_decoder_scher_khan_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + instance->decoder.parser_step = ScherKhanDecoderStepReset; +} + +void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + + switch(instance->decoder.parser_step) { + case ScherKhanDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) < + subghz_protocol_scher_khan_const.te_delta)) { + instance->decoder.parser_step = ScherKhanDecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + case ScherKhanDecoderStepCheckPreambula: + if(level) { + if((DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) < + subghz_protocol_scher_khan_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) < + subghz_protocol_scher_khan_const.te_delta)) { + instance->decoder.te_last = duration; + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + } else if( + (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) < + subghz_protocol_scher_khan_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) < + subghz_protocol_scher_khan_const.te_delta)) { + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short * 2) < + subghz_protocol_scher_khan_const.te_delta) { + // Found header + instance->header_count++; + break; + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) < + subghz_protocol_scher_khan_const.te_delta) { + // Found start bit + if(instance->header_count >= 2) { + instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 1; + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + break; + case ScherKhanDecoderStepSaveDuration: + if(level) { + if(duration >= (subghz_protocol_scher_khan_const.te_delta * 2UL + + subghz_protocol_scher_khan_const.te_long)) { + //Found stop bit + instance->decoder.parser_step = ScherKhanDecoderStepReset; + if(instance->decoder.decode_count_bit >= + subghz_protocol_scher_khan_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = ScherKhanDecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + break; + case ScherKhanDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) < + subghz_protocol_scher_khan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) < + subghz_protocol_scher_khan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_scher_khan_const.te_long) < + subghz_protocol_scher_khan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_long) < + subghz_protocol_scher_khan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param protocol_name + */ +static void subghz_protocol_scher_khan_check_remote_controller( + SubGhzBlockGeneric* instance, + const char** protocol_name) { + /* + * MAGICAR 51 bit 00000001A99121DE83C3 MAGIC CODE, Dynamic + * 0E8C1619E830C -> 000011101000110000010110 0001 1001 1110 1000001100001100 + * 0E8C1629D830D -> 000011101000110000010110 0010 1001 1101 1000001100001101 + * 0E8C1649B830E -> 000011101000110000010110 0100 1001 1011 1000001100001110 + * 0E8C16897830F -> 000011101000110000010110 1000 1001 0111 1000001100001111 + * Serial Key Ser ~Key CNT + */ + + switch(instance->data_count_bit) { + // case 35: //MAGIC CODE, Static + // instance->protocol_name = "MAGIC CODE, Static"; + // break; + case 51: //MAGIC CODE, Dynamic + *protocol_name = "MAGIC CODE, Dynamic"; + instance->serial = ((instance->data >> 24) & 0xFFFFFF0) | ((instance->data >> 20) & 0x0F); + instance->btn = (instance->data >> 24) & 0x0F; + instance->cnt = instance->data & 0xFFFF; + break; + // case 57: //MAGIC CODE PRO / PRO2 + // instance->protocol_name = "MAGIC CODE PRO / PRO2"; + // break; + + default: + *protocol_name = "Unknown"; + instance->serial = 0; + instance->btn = 0; + instance->cnt = 0; + break; + } +} + +uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_scher_khan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + return subghz_block_generic_deserialize(&instance->generic, flipper_format); +} + +void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + + subghz_protocol_scher_khan_check_remote_controller( + &instance->generic, &instance->protocol_name); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:%07lX Btn:%X Cnt:%04lX\r\n" + "Pt: %s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt, + instance->protocol_name); +} diff --git a/applications/main/subghz/protocols/scher_khan.h b/applications/main/subghz/protocols/scher_khan.h new file mode 100644 index 000000000..b7e84ea1f --- /dev/null +++ b/applications/main/subghz/protocols/scher_khan.h @@ -0,0 +1,73 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_SCHER_KHAN_NAME "Scher-Khan" + +typedef struct SubGhzProtocolDecoderScherKhan SubGhzProtocolDecoderScherKhan; +typedef struct SubGhzProtocolEncoderScherKhan SubGhzProtocolEncoderScherKhan; + +extern const SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder; +extern const SubGhzProtocol subghz_protocol_scher_khan; + +/** + * Allocate SubGhzProtocolDecoderScherKhan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderScherKhan* pointer to a SubGhzProtocolDecoderScherKhan instance + */ +void* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderScherKhan. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + */ +void subghz_protocol_decoder_scher_khan_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderScherKhan. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + */ +void subghz_protocol_decoder_scher_khan_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderScherKhan. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_scher_khan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderScherKhan. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + * @param output Resulting text + */ +void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/secplus_v1.c b/applications/main/subghz/protocols/secplus_v1.c new file mode 100644 index 000000000..3ef95db36 --- /dev/null +++ b/applications/main/subghz/protocols/secplus_v1.c @@ -0,0 +1,634 @@ +#include "secplus_v1.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* +* Help +* https://github.com/argilo/secplus +* https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v1.c +*/ + +#define TAG "SubGhzProtocoSecPlus_v1" + +#define SECPLUS_V1_BIT_ERR -1 //0b0000 +#define SECPLUS_V1_BIT_0 0 //0b0001 +#define SECPLUS_V1_BIT_1 1 //0b0011 +#define SECPLUS_V1_BIT_2 2 //0b0111 + +#define SECPLUS_V1_PACKET_1_HEADER 0x00 +#define SECPLUS_V1_PACKET_2_HEADER 0x02 +#define SECPLUS_V1_PACKET_1_INDEX_BASE 0 +#define SECPLUS_V1_PACKET_2_INDEX_BASE 21 +#define SECPLUS_V1_PACKET_1_ACCEPTED (1 << 0) +#define SECPLUS_V1_PACKET_2_ACCEPTED (1 << 1) + +static const SubGhzBlockConst subghz_protocol_secplus_v1_const = { + .te_short = 500, + .te_long = 1500, + .te_delta = 100, + .min_count_bit_for_found = 21, +}; + +struct SubGhzProtocolDecoderSecPlus_v1 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint8_t packet_accepted; + uint8_t base_packet_index; + uint8_t data_array[44]; +}; + +struct SubGhzProtocolEncoderSecPlus_v1 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint8_t data_array[44]; +}; + +typedef enum { + SecPlus_v1DecoderStepReset = 0, + SecPlus_v1DecoderStepSearchStartBit, + SecPlus_v1DecoderStepSaveDuration, + SecPlus_v1DecoderStepDecoderData, +} SecPlus_v1DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder = { + .alloc = subghz_protocol_decoder_secplus_v1_alloc, + .free = subghz_protocol_decoder_secplus_v1_free, + + .feed = subghz_protocol_decoder_secplus_v1_feed, + .reset = subghz_protocol_decoder_secplus_v1_reset, + + .get_hash_data = subghz_protocol_decoder_secplus_v1_get_hash_data, + .serialize = subghz_protocol_decoder_secplus_v1_serialize, + .deserialize = subghz_protocol_decoder_secplus_v1_deserialize, + .get_string = subghz_protocol_decoder_secplus_v1_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder = { + .alloc = subghz_protocol_encoder_secplus_v1_alloc, + .free = subghz_protocol_encoder_secplus_v1_free, + + .deserialize = subghz_protocol_encoder_secplus_v1_deserialize, + .stop = subghz_protocol_encoder_secplus_v1_stop, + .yield = subghz_protocol_encoder_secplus_v1_yield, +}; + +const SubGhzProtocol subghz_protocol_secplus_v1 = { + .name = SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Send | SubGhzProtocolFlag_Save, + + .decoder = &subghz_protocol_secplus_v1_decoder, + .encoder = &subghz_protocol_secplus_v1_encoder, +}; + +void* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v1)); + + instance->base.protocol = &subghz_protocol_secplus_v1; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_secplus_v1_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v1* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_secplus_v1_get_upload(SubGhzProtocolEncoderSecPlus_v1* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Encoder size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header packet 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116 + 3)); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + + //Send data packet 1 + for(uint8_t i = SECPLUS_V1_PACKET_1_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_1_INDEX_BASE + 21; + i++) { + switch(instance->data_array[i]) { + case SECPLUS_V1_BIT_0: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + break; + case SECPLUS_V1_BIT_1: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + break; + case SECPLUS_V1_BIT_2: + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + break; + + default: + FURI_LOG_E(TAG, "Encoder error, wrong bit type"); + return false; + break; + } + } + + //Send header packet 2 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116)); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + + //Send data packet 2 + for(uint8_t i = SECPLUS_V1_PACKET_2_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_2_INDEX_BASE + 21; + i++) { + switch(instance->data_array[i]) { + case SECPLUS_V1_BIT_0: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + break; + case SECPLUS_V1_BIT_1: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + break; + case SECPLUS_V1_BIT_2: + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + break; + + default: + FURI_LOG_E(TAG, "Encoder error, wrong bit type."); + return false; + break; + } + } + + return true; +} + +/** + * Security+ 1.0 message encoding + * @param instance SubGhzProtocolEncoderSecPlus_v1* + */ + +static bool subghz_protocol_secplus_v1_encode(SubGhzProtocolEncoderSecPlus_v1* instance) { + uint32_t fixed = (instance->generic.data >> 32) & 0xFFFFFFFF; + uint32_t rolling = instance->generic.data & 0xFFFFFFFF; + + uint8_t rolling_array[20] = {0}; + uint8_t fixed_array[20] = {0}; + uint32_t acc = 0; + + //increment the counter + rolling += 2; + + //update data + instance->generic.data &= 0xFFFFFFFF00000000; + instance->generic.data |= rolling; + + if(rolling == 0xFFFFFFFF) { + rolling = 0xE6000000; + } + if(fixed > 0xCFD41B90) { + FURI_LOG_E("TAG", "Encode wrong fixed data"); + return false; + } + + rolling = subghz_protocol_blocks_reverse_key(rolling, 32); + + for(int i = 19; i > -1; i--) { + rolling_array[i] = rolling % 3; + rolling /= 3; + fixed_array[i] = fixed % 3; + fixed /= 3; + } + + instance->data_array[SECPLUS_V1_PACKET_1_INDEX_BASE] = SECPLUS_V1_PACKET_1_HEADER; + instance->data_array[SECPLUS_V1_PACKET_2_INDEX_BASE] = SECPLUS_V1_PACKET_2_HEADER; + + //encode packet 1 + for(uint8_t i = 1; i < 11; i++) { + acc += rolling_array[i - 1]; + instance->data_array[i * 2 - 1] = rolling_array[i - 1]; + acc += fixed_array[i - 1]; + instance->data_array[i * 2] = acc % 3; + } + + acc = 0; + //encode packet 2 + for(uint8_t i = 11; i < 21; i++) { + acc += rolling_array[i - 1]; + instance->data_array[i * 2] = rolling_array[i - 1]; + acc += fixed_array[i - 1]; + instance->data_array[i * 2 + 1] = acc % 3; + } + + return true; +} + +bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v1* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + 2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_secplus_v1_encode(instance)) { + break; + } + if(!subghz_protocol_encoder_secplus_v1_get_upload(instance)) { + break; + } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_secplus_v1_stop(void* context) { + SubGhzProtocolEncoderSecPlus_v1* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context) { + SubGhzProtocolEncoderSecPlus_v1* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_secplus_v1_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v1)); + instance->base.protocol = &subghz_protocol_secplus_v1; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_secplus_v1_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + free(instance); +} + +void subghz_protocol_decoder_secplus_v1_reset(void* context) { + furi_assert(context); + // SubGhzProtocolDecoderSecPlus_v1* instance = context; + // does not reset the decoder because you need to get 2 parts of the package +} + +/** + * Security+ 1.0 message decoding + * @param instance SubGhzProtocolDecoderSecPlus_v1* + */ + +static void subghz_protocol_secplus_v1_decode(SubGhzProtocolDecoderSecPlus_v1* instance) { + uint32_t rolling = 0; + uint32_t fixed = 0; + uint32_t acc = 0; + uint8_t digit = 0; + + //decode packet 1 + for(uint8_t i = 1; i < 21; i += 2) { + digit = instance->data_array[i]; + rolling = (rolling * 3) + digit; + acc += digit; + + digit = (60 + instance->data_array[i + 1] - acc) % 3; + fixed = (fixed * 3) + digit; + acc += digit; + } + + acc = 0; + //decode packet 2 + for(uint8_t i = 22; i < 42; i += 2) { + digit = instance->data_array[i]; + rolling = (rolling * 3) + digit; + acc += digit; + + digit = (60 + instance->data_array[i + 1] - acc) % 3; + fixed = (fixed * 3) + digit; + acc += digit; + } + + rolling = subghz_protocol_blocks_reverse_key(rolling, 32); + instance->generic.data = (uint64_t)fixed << 32 | rolling; + + instance->generic.data_count_bit = + subghz_protocol_secplus_v1_const.min_count_bit_for_found * 2; +} + +void subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + + switch(instance->decoder.parser_step) { + case SecPlus_v1DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 120) < + subghz_protocol_secplus_v1_const.te_delta * 120)) { + //Found header Security+ 1.0 + instance->decoder.parser_step = SecPlus_v1DecoderStepSearchStartBit; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->packet_accepted = 0; + memset(instance->data_array, 0, sizeof(instance->data_array)); + } + break; + case SecPlus_v1DecoderStepSearchStartBit: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short) < + subghz_protocol_secplus_v1_const.te_delta) { + instance->base_packet_index = SECPLUS_V1_PACKET_1_INDEX_BASE; + instance + ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = + SECPLUS_V1_BIT_0; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_long) < + subghz_protocol_secplus_v1_const.te_delta) { + instance->base_packet_index = SECPLUS_V1_PACKET_2_INDEX_BASE; + instance + ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = + SECPLUS_V1_BIT_2; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + } else { + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + break; + case SecPlus_v1DecoderStepSaveDuration: + if(!level) { //save interval + if(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 120) < + subghz_protocol_secplus_v1_const.te_delta * 120) { + if(instance->decoder.decode_count_bit == + subghz_protocol_secplus_v1_const.min_count_bit_for_found) { + if(instance->base_packet_index == SECPLUS_V1_PACKET_1_INDEX_BASE) + instance->packet_accepted |= SECPLUS_V1_PACKET_1_ACCEPTED; + if(instance->base_packet_index == SECPLUS_V1_PACKET_2_INDEX_BASE) + instance->packet_accepted |= SECPLUS_V1_PACKET_2_ACCEPTED; + + if(instance->packet_accepted == + (SECPLUS_V1_PACKET_1_ACCEPTED | SECPLUS_V1_PACKET_2_ACCEPTED)) { + subghz_protocol_secplus_v1_decode(instance); + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + } + instance->decoder.parser_step = SecPlus_v1DecoderStepSearchStartBit; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = SecPlus_v1DecoderStepDecoderData; + } + } else { + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + break; + case SecPlus_v1DecoderStepDecoderData: + if(level && (instance->decoder.decode_count_bit <= + subghz_protocol_secplus_v1_const.min_count_bit_for_found)) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short * 3) < + subghz_protocol_secplus_v1_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short) < + subghz_protocol_secplus_v1_const.te_delta)) { + instance + ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = + SECPLUS_V1_BIT_0; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short * 2) < + subghz_protocol_secplus_v1_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 2) < + subghz_protocol_secplus_v1_const.te_delta * 2)) { + instance + ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = + SECPLUS_V1_BIT_1; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short) < + subghz_protocol_secplus_v1_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 3) < + subghz_protocol_secplus_v1_const.te_delta * 3)) { + instance + ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = + SECPLUS_V1_BIT_2; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + } else { + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_secplus_v1_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + 2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed) { + //uint8_t id0 = (fixed / 3) % 3; + uint8_t id1 = (fixed / 9) % 3; + uint8_t btn = fixed % 3; + + do { + if(id1 == 0) return false; + if(!(btn == 0 || btn == 1 || btn == 2)) return false; //-V560 + } while(false); + return true; +} + +void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + + uint32_t fixed = (instance->generic.data >> 32) & 0xFFFFFFFF; + instance->generic.cnt = instance->generic.data & 0xFFFFFFFF; + + instance->generic.btn = fixed % 3; + uint8_t id0 = (fixed / 3) % 3; + uint8_t id1 = (fixed / 9) % 3; + uint16_t pin = 0; + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "id1:%d id0:%d", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + id1, + id0); + + if(id1 == 0) { + // (fixed // 3**3) % (3**7) 3^3=27 3^73=72187 + + instance->generic.serial = (fixed / 27) % 2187; + // pin = (fixed // 3**10) % (3**9) 3^10=59049 3^9=19683 + pin = (fixed / 59049) % 19683; + + if(pin <= 9999) { + furi_string_cat_printf(output, " pin:%d", pin); + } else if(pin <= 11029) { + furi_string_cat_printf(output, " pin:enter"); + } + + int pin_suffix = 0; + // pin_suffix = (fixed // 3**19) % 3 3^19=1162261467 + pin_suffix = (fixed / 1162261467) % 3; + + if(pin_suffix == 1) { + furi_string_cat_printf(output, " #\r\n"); + } else if(pin_suffix == 2) { + furi_string_cat_printf(output, " *\r\n"); + } else { + furi_string_cat_printf(output, "\r\n"); + } + furi_string_cat_printf( + output, + "Sn:0x%08lX\r\n" + "Cnt:0x%03lX " + "Sw_id:0x%X\r\n", + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } else { + //id = fixed / 27; + instance->generic.serial = fixed / 27; + if(instance->generic.btn == 1) { + furi_string_cat_printf(output, " Btn:left\r\n"); + } else if(instance->generic.btn == 0) { + furi_string_cat_printf(output, " Btn:middle\r\n"); + } else if(instance->generic.btn == 2) { //-V547 + furi_string_cat_printf(output, " Btn:right\r\n"); + } + + furi_string_cat_printf( + output, + "Sn:0x%08lX\r\n" + "Cnt:0x%03lX " + "Sw_id:0x%X\r\n", + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } +} diff --git a/applications/main/subghz/protocols/secplus_v1.h b/applications/main/subghz/protocols/secplus_v1.h new file mode 100644 index 000000000..99480b62b --- /dev/null +++ b/applications/main/subghz/protocols/secplus_v1.h @@ -0,0 +1,113 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_SECPLUS_V1_NAME "Security+ 1.0" + +typedef struct SubGhzProtocolDecoderSecPlus_v1 SubGhzProtocolDecoderSecPlus_v1; +typedef struct SubGhzProtocolEncoderSecPlus_v1 SubGhzProtocolEncoderSecPlus_v1; + +extern const SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder; +extern const SubGhzProtocol subghz_protocol_secplus_v1; + +/** + * Allocate SubGhzProtocolEncoderSecPlus_v1. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSecPlus_v1* pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + */ +void* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSecPlus_v1. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + */ +void subghz_protocol_encoder_secplus_v1_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + */ +void subghz_protocol_encoder_secplus_v1_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderSecPlus_v1. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSecPlus_v1* pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + */ +void* subghz_protocol_decoder_secplus_v1_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSecPlus_v1. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + */ +void subghz_protocol_decoder_secplus_v1_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSecPlus_v1. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + */ +void subghz_protocol_decoder_secplus_v1_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSecPlus_v1. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_secplus_v1_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSecPlus_v1. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Validation of fixed parts SubGhzProtocolDecoderSecPlus_v1. + * @param fixed fixed parts + * @return true On success + */ +bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/secplus_v2.c b/applications/main/subghz/protocols/secplus_v2.c new file mode 100644 index 000000000..bcef90dad --- /dev/null +++ b/applications/main/subghz/protocols/secplus_v2.c @@ -0,0 +1,833 @@ +#include "secplus_v2.h" +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* +* Help +* https://github.com/argilo/secplus +* https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v2.c +*/ + +#define TAG "SubGhzProtocoSecPlus_v2" + +#define SECPLUS_V2_HEADER 0x3C0000000000 +#define SECPLUS_V2_HEADER_MASK 0xFFFF3C0000000000 +#define SECPLUS_V2_PACKET_1 0x000000000000 +#define SECPLUS_V2_PACKET_2 0x010000000000 +#define SECPLUS_V2_PACKET_MASK 0x30000000000 + +static const SubGhzBlockConst subghz_protocol_secplus_v2_const = { + .te_short = 250, + .te_long = 500, + .te_delta = 110, + .min_count_bit_for_found = 62, +}; + +struct SubGhzProtocolDecoderSecPlus_v2 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + ManchesterState manchester_saved_state; + uint64_t secplus_packet_1; +}; + +struct SubGhzProtocolEncoderSecPlus_v2 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + uint64_t secplus_packet_1; +}; + +typedef enum { + SecPlus_v2DecoderStepReset = 0, + SecPlus_v2DecoderStepDecoderData, +} SecPlus_v2DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder = { + .alloc = subghz_protocol_decoder_secplus_v2_alloc, + .free = subghz_protocol_decoder_secplus_v2_free, + + .feed = subghz_protocol_decoder_secplus_v2_feed, + .reset = subghz_protocol_decoder_secplus_v2_reset, + + .get_hash_data = subghz_protocol_decoder_secplus_v2_get_hash_data, + .serialize = subghz_protocol_decoder_secplus_v2_serialize, + .deserialize = subghz_protocol_decoder_secplus_v2_deserialize, + .get_string = subghz_protocol_decoder_secplus_v2_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder = { + .alloc = subghz_protocol_encoder_secplus_v2_alloc, + .free = subghz_protocol_encoder_secplus_v2_free, + + .deserialize = subghz_protocol_encoder_secplus_v2_deserialize, + .stop = subghz_protocol_encoder_secplus_v2_stop, + .yield = subghz_protocol_encoder_secplus_v2_yield, +}; + +const SubGhzProtocol subghz_protocol_secplus_v2 = { + .name = SUBGHZ_PROTOCOL_SECPLUS_V2_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_secplus_v2_decoder, + .encoder = &subghz_protocol_secplus_v2_encoder, +}; + +void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v2)); + + instance->base.protocol = &subghz_protocol_secplus_v2; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_secplus_v2_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v2* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static bool subghz_protocol_secplus_v2_mix_invet(uint8_t invert, uint16_t p[]) { + // selectively invert buffers + switch(invert) { + case 0x00: // 0b0000 (True, True, False), + p[0] = ~p[0] & 0x03FF; + p[1] = ~p[1] & 0x03FF; + break; + case 0x01: // 0b0001 (False, True, False), + p[1] = ~p[1] & 0x03FF; + break; + case 0x02: // 0b0010 (False, False, True), + p[2] = ~p[2] & 0x03FF; + break; + case 0x04: // 0b0100 (True, True, True), + p[0] = ~p[0] & 0x03FF; + p[1] = ~p[1] & 0x03FF; + p[2] = ~p[2] & 0x03FF; + break; + case 0x05: // 0b0101 (True, False, True), + case 0x0a: // 0b1010 (True, False, True), + p[0] = ~p[0] & 0x03FF; + p[2] = ~p[2] & 0x03FF; + break; + case 0x06: // 0b0110 (False, True, True), + p[1] = ~p[1] & 0x03FF; + p[2] = ~p[2] & 0x03FF; + break; + case 0x08: // 0b1000 (True, False, False), + p[0] = ~p[0] & 0x03FF; + break; + case 0x09: // 0b1001 (False, False, False), + break; + default: + FURI_LOG_E(TAG, "Invert FAIL"); + return false; + } + return true; +} + +static bool subghz_protocol_secplus_v2_mix_order_decode(uint8_t order, uint16_t p[]) { + uint16_t a = p[0], b = p[1], c = p[2]; + + // selectively reorder buffers + switch(order) { + case 0x06: // 0b0110 2, 1, 0], + case 0x09: // 0b1001 2, 1, 0], + p[2] = a; + // p[1]: no change + p[0] = c; + break; + case 0x08: // 0b1000 1, 2, 0], + case 0x04: // 0b0100 1, 2, 0], + p[1] = a; + p[2] = b; + p[0] = c; + break; + case 0x01: // 0b0001 2, 0, 1], + p[2] = a; + p[0] = b; + p[1] = c; + break; + case 0x00: // 0b0000 0, 2, 1], + // p[0]: no change + p[2] = b; + p[1] = c; + break; + case 0x05: // 0b0101 1, 0, 2], + p[1] = a; + p[0] = b; + // p[2]: no change + break; + case 0x02: // 0b0010 0, 1, 2], + case 0x0A: // 0b1010 0, 1, 2], + // no reordering + break; + default: + FURI_LOG_E(TAG, "Order FAIL"); + return false; + } + return true; +} + +static bool subghz_protocol_secplus_v2_mix_order_encode(uint8_t order, uint16_t p[]) { + uint16_t a, b, c; + + // selectively reorder buffers + switch(order) { + case 0x06: // 0b0110 2, 1, 0], + case 0x09: // 0b1001 2, 1, 0], + a = p[2]; + b = p[1]; + c = p[0]; + break; + case 0x08: // 0b1000 1, 2, 0], + case 0x04: // 0b0100 1, 2, 0], + a = p[1]; + b = p[2]; + c = p[0]; + break; + case 0x01: // 0b0001 2, 0, 1], + a = p[2]; + b = p[0]; + c = p[1]; + break; + case 0x00: // 0b0000 0, 2, 1], + a = p[0]; + b = p[2]; + c = p[1]; + break; + case 0x05: // 0b0101 1, 0, 2], + a = p[1]; + b = p[0]; + c = p[2]; + break; + case 0x02: // 0b0010 0, 1, 2], + case 0x0A: // 0b1010 0, 1, 2], + a = p[0]; + b = p[1]; + c = p[2]; + break; + default: + FURI_LOG_E(TAG, "Order FAIL"); + return false; + } + + p[0] = a; + p[1] = b; + p[2] = c; + return true; +} + +/** + * Security+ 2.0 half-message decoding + * @param data data + * @param roll_array[] return roll_array part + * @param fixed[] return fixed part + * @return true On success + */ + +static bool + subghz_protocol_secplus_v2_decode_half(uint64_t data, uint8_t roll_array[], uint32_t* fixed) { + uint8_t order = (data >> 34) & 0x0f; + uint8_t invert = (data >> 30) & 0x0f; + uint16_t p[3] = {0}; + + for(int i = 29; i >= 0; i -= 3) { + p[0] = p[0] << 1 | bit_read(data, i); + p[1] = p[1] << 1 | bit_read(data, i - 1); + p[2] = p[2] << 1 | bit_read(data, i - 2); + } + + if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return false; + if(!subghz_protocol_secplus_v2_mix_order_decode(order, p)) return false; + + data = order << 4 | invert; + int k = 0; + for(int i = 6; i >= 0; i -= 2) { + roll_array[k] = (data >> i) & 0x03; + if(roll_array[k++] == 3) { + FURI_LOG_E(TAG, "Roll_Array FAIL"); + return false; + } + } + + for(int i = 8; i >= 0; i -= 2) { + roll_array[k] = (p[2] >> i) & 0x03; + if(roll_array[k++] == 3) { + FURI_LOG_E(TAG, "Roll_Array FAIL"); + return false; + } + } + + fixed[0] = p[0] << 10 | p[1]; + return true; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param packet_1 first part of the message + */ +static void + subghz_protocol_secplus_v2_remote_controller(SubGhzBlockGeneric* instance, uint64_t packet_1) { + uint32_t fixed_1[1]; + uint8_t roll_1[9] = {0}; + uint32_t fixed_2[1]; + uint8_t roll_2[9] = {0}; + uint8_t rolling_digits[18] = {0}; + + if(subghz_protocol_secplus_v2_decode_half(packet_1, roll_1, fixed_1) && + subghz_protocol_secplus_v2_decode_half(instance->data, roll_2, fixed_2)) { + rolling_digits[0] = roll_2[8]; + rolling_digits[1] = roll_1[8]; + + rolling_digits[2] = roll_2[4]; + rolling_digits[3] = roll_2[5]; + rolling_digits[4] = roll_2[6]; + rolling_digits[5] = roll_2[7]; + + rolling_digits[6] = roll_1[4]; + rolling_digits[7] = roll_1[5]; + rolling_digits[8] = roll_1[6]; + rolling_digits[9] = roll_1[7]; + + rolling_digits[10] = roll_2[0]; + rolling_digits[11] = roll_2[1]; + rolling_digits[12] = roll_2[2]; + rolling_digits[13] = roll_2[3]; + + rolling_digits[14] = roll_1[0]; + rolling_digits[15] = roll_1[1]; + rolling_digits[16] = roll_1[2]; + rolling_digits[17] = roll_1[3]; + + uint32_t rolling = 0; + for(int i = 0; i < 18; i++) { + rolling = (rolling * 3) + rolling_digits[i]; + } + // Max value = 2^28 (268435456) + if(rolling >= 0x10000000) { + FURI_LOG_E(TAG, "Rolling FAIL"); + instance->cnt = 0; + instance->btn = 0; + instance->serial = 0; + } else { + instance->cnt = subghz_protocol_blocks_reverse_key(rolling, 28); + instance->btn = fixed_1[0] >> 12; + instance->serial = fixed_1[0] << 20 | fixed_2[0]; + } + } else { + instance->cnt = 0; + instance->btn = 0; + instance->serial = 0; + } +} + +/** + * Security+ 2.0 half-message encoding + * @param roll_array[] roll_array part + * @param fixed[] fixed part + * @return return data + */ + +static uint64_t subghz_protocol_secplus_v2_encode_half(uint8_t roll_array[], uint32_t fixed) { + uint64_t data = 0; + uint16_t p[3] = {(fixed >> 10) & 0x3FF, fixed & 0x3FF, 0}; + uint8_t order = roll_array[0] << 2 | roll_array[1]; + uint8_t invert = roll_array[2] << 2 | roll_array[3]; + p[2] = (uint16_t)roll_array[4] << 8 | roll_array[5] << 6 | roll_array[6] << 4 | + roll_array[7] << 2 | roll_array[8]; + + if(!subghz_protocol_secplus_v2_mix_order_encode(order, p)) return 0; + if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return 0; + + for(int i = 0; i < 10; i++) { + data <<= 3; + data |= bit_read(p[0], 9 - i) << 2 | bit_read(p[1], 9 - i) << 1 | bit_read(p[2], 9 - i); + } + data |= ((uint64_t)order) << 34 | ((uint64_t)invert) << 30; + + return data; +} + +/** + * Security+ 2.0 message encoding + * @param instance SubGhzProtocolEncoderSecPlus_v2* + */ + +static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* instance) { + uint32_t fixed_1[1] = {instance->generic.btn << 12 | instance->generic.serial >> 20}; + uint32_t fixed_2[1] = {instance->generic.serial & 0xFFFFF}; + uint8_t rolling_digits[18] = {0}; + uint8_t roll_1[9] = {0}; + uint8_t roll_2[9] = {0}; + + instance->generic.cnt++; + //ToDo it is not known what value the counter starts + if(instance->generic.cnt > 0xFFFFFFF) instance->generic.cnt = 0xE500000; + uint32_t rolling = subghz_protocol_blocks_reverse_key(instance->generic.cnt, 28); + + for(int8_t i = 17; i > -1; i--) { + rolling_digits[i] = rolling % 3; + rolling /= 3; + } + + roll_2[8] = rolling_digits[0]; + roll_1[8] = rolling_digits[1]; + + roll_2[4] = rolling_digits[2]; + roll_2[5] = rolling_digits[3]; + roll_2[6] = rolling_digits[4]; + roll_2[7] = rolling_digits[5]; + + roll_1[4] = rolling_digits[6]; + roll_1[5] = rolling_digits[7]; + roll_1[6] = rolling_digits[8]; + roll_1[7] = rolling_digits[9]; + + roll_2[0] = rolling_digits[10]; + roll_2[1] = rolling_digits[11]; + roll_2[2] = rolling_digits[12]; + roll_2[3] = rolling_digits[13]; + + roll_1[0] = rolling_digits[14]; + roll_1[1] = rolling_digits[15]; + roll_1[2] = rolling_digits[16]; + roll_1[3] = rolling_digits[17]; + + instance->secplus_packet_1 = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_1 | + subghz_protocol_secplus_v2_encode_half(roll_1, fixed_1[0]); + instance->generic.data = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_2 | + subghz_protocol_secplus_v2_encode_half(roll_2, fixed_2[0]); +} + +static LevelDuration + subghz_protocol_encoder_secplus_v2_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_secplus_v2_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_secplus_v2_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_secplus_v2_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_secplus_v2_const.te_short; + data.level = true; + break; + + default: + furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + */ +static void + subghz_protocol_encoder_secplus_v2_get_upload(SubGhzProtocolEncoderSecPlus_v2* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + //Send data packet 1 + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); + } + instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136); + + //Send data packet 2 + manchester_encoder_reset(&enc_state); + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); + } + instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136); + + instance->encoder.size_upload = index; +} + +bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v2* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_secplus_v2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex( + flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Secplus_packet_1"); + break; + } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i]; + } + + subghz_protocol_secplus_v2_remote_controller( + &instance->generic, instance->secplus_packet_1); + subghz_protocol_secplus_v2_encode(instance); + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + subghz_protocol_encoder_secplus_v2_get_upload(instance); + + //update data + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; + } + if(!flipper_format_update_hex( + flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_secplus_v2_stop(void* context) { + SubGhzProtocolEncoderSecPlus_v2* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context) { + SubGhzProtocolEncoderSecPlus_v2* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +bool subghz_protocol_secplus_v2_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint32_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v2* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.btn = btn; + instance->generic.data_count_bit = + (uint8_t)subghz_protocol_secplus_v2_const.min_count_bit_for_found; + subghz_protocol_secplus_v2_encode(instance); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; + } + + if(res && + !flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); + res = false; + } + return res; +} + +void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v2)); + instance->base.protocol = &subghz_protocol_secplus_v2; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_secplus_v2_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* instance = context; + free(instance); +} + +void subghz_protocol_decoder_secplus_v2_reset(void* context) { + furi_assert(context); + // SubGhzProtocolDecoderSecPlus_v2* instance = context; + // does not reset the decoder because you need to get 2 parts of the package +} + +static bool subghz_protocol_secplus_v2_check_packet(SubGhzProtocolDecoderSecPlus_v2* instance) { + if((instance->decoder.decode_data & SECPLUS_V2_HEADER_MASK) == SECPLUS_V2_HEADER) { + if((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_1) { + instance->secplus_packet_1 = instance->decoder.decode_data; + } else if( + ((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_2) && + (instance->secplus_packet_1)) { + return true; + } + } + return false; +} + +void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* instance = context; + + ManchesterEvent event = ManchesterEventReset; + switch(instance->decoder.parser_step) { + case SecPlus_v2DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long * 130) < + subghz_protocol_secplus_v2_const.te_delta * 100)) { + //Found header Security+ 2.0 + instance->decoder.parser_step = SecPlus_v2DecoderStepDecoderData; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->secplus_packet_1 = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } + break; + case SecPlus_v2DecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) < + subghz_protocol_secplus_v2_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) < + subghz_protocol_secplus_v2_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= (subghz_protocol_secplus_v2_const.te_long * 2UL + + subghz_protocol_secplus_v2_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_secplus_v2_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(subghz_protocol_secplus_v2_check_packet(instance)) { + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.parser_step = SecPlus_v2DecoderStepReset; + } + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } else { + instance->decoder.parser_step = SecPlus_v2DecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) < + subghz_protocol_secplus_v2_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) < + subghz_protocol_secplus_v2_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = SecPlus_v2DecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_secplus_v2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; + } + + if(res && + !flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_secplus_v2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex( + flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Secplus_packet_1"); + break; + } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i]; + } + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* instance = context; + subghz_protocol_secplus_v2_remote_controller(&instance->generic, instance->secplus_packet_1); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Pk1:0x%lX%08lX\r\n" + "Pk2:0x%lX%08lX\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%03lX\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->secplus_packet_1 >> 32), + (uint32_t)instance->secplus_packet_1, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/applications/main/subghz/protocols/secplus_v2.h b/applications/main/subghz/protocols/secplus_v2.h new file mode 100644 index 000000000..bea8cdb5d --- /dev/null +++ b/applications/main/subghz/protocols/secplus_v2.h @@ -0,0 +1,125 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_SECPLUS_V2_NAME "Security+ 2.0" + +typedef struct SubGhzProtocolDecoderSecPlus_v2 SubGhzProtocolDecoderSecPlus_v2; +typedef struct SubGhzProtocolEncoderSecPlus_v2 SubGhzProtocolEncoderSecPlus_v2; + +extern const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder; +extern const SubGhzProtocol subghz_protocol_secplus_v2; + +/** + * Allocate SubGhzProtocolEncoderSecPlus_v2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSecPlus_v2* pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + */ +void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSecPlus_v2. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + */ +void subghz_protocol_encoder_secplus_v2_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + */ +void subghz_protocol_encoder_secplus_v2_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 32 bit + * @param btn Button number, 8 bit + * @param cnt Container value, 28 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_secplus_v2_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint32_t cnt, + SubGhzRadioPreset* preset); + +/** + * Allocate SubGhzProtocolDecoderSecPlus_v2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSecPlus_v2* pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + */ +void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSecPlus_v2. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + */ +void subghz_protocol_decoder_secplus_v2_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSecPlus_v2. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + */ +void subghz_protocol_decoder_secplus_v2_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSecPlus_v2. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_secplus_v2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSecPlus_v2. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/smc5326.c b/applications/main/subghz/protocols/smc5326.c new file mode 100644 index 000000000..9c9b5d4fd --- /dev/null +++ b/applications/main/subghz/protocols/smc5326.c @@ -0,0 +1,387 @@ +#include "smc5326.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://datasheetspdf.com/pdf-file/532079/Aslic/AX5326-4/1 + * + */ + +#define TAG "SubGhzProtocolSMC5326" + +#define DIP_P 0b11 //(+) +#define DIP_O 0b10 //(0) +#define DIP_N 0b00 //(-) + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define SHOW_DIP_P(dip, check_dip) \ + ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_') + +static const SubGhzBlockConst subghz_protocol_smc5326_const = { + .te_short = 300, + .te_long = 900, + .te_delta = 200, + .min_count_bit_for_found = 25, +}; + +struct SubGhzProtocolDecoderSMC5326 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t te; + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderSMC5326 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint32_t te; +}; + +typedef enum { + SMC5326DecoderStepReset = 0, + SMC5326DecoderStepSaveDuration, + SMC5326DecoderStepCheckDuration, +} SMC5326DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_smc5326_decoder = { + .alloc = subghz_protocol_decoder_smc5326_alloc, + .free = subghz_protocol_decoder_smc5326_free, + + .feed = subghz_protocol_decoder_smc5326_feed, + .reset = subghz_protocol_decoder_smc5326_reset, + + .get_hash_data = subghz_protocol_decoder_smc5326_get_hash_data, + .serialize = subghz_protocol_decoder_smc5326_serialize, + .deserialize = subghz_protocol_decoder_smc5326_deserialize, + .get_string = subghz_protocol_decoder_smc5326_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_smc5326_encoder = { + .alloc = subghz_protocol_encoder_smc5326_alloc, + .free = subghz_protocol_encoder_smc5326_free, + + .deserialize = subghz_protocol_encoder_smc5326_deserialize, + .stop = subghz_protocol_encoder_smc5326_stop, + .yield = subghz_protocol_encoder_smc5326_yield, +}; + +const SubGhzProtocol subghz_protocol_smc5326 = { + .name = SUBGHZ_PROTOCOL_SMC5326_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_smc5326_decoder, + .encoder = &subghz_protocol_smc5326_encoder, +}; + +void* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSMC5326* instance = malloc(sizeof(SubGhzProtocolEncoderSMC5326)); + + instance->base.protocol = &subghz_protocol_smc5326; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_smc5326_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSMC5326* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderSMC5326 instance + * @return true On success + */ +static bool subghz_protocol_encoder_smc5326_get_upload(SubGhzProtocolEncoderSMC5326* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)instance->te * 3); + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)instance->te * 3); + } + } + + //Send Stop bit + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + //Send PT_GUARD + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 25); + + return true; +} + +bool subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSMC5326* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_smc5326_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_smc5326_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_smc5326_stop(void* context) { + SubGhzProtocolEncoderSMC5326* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_smc5326_yield(void* context) { + SubGhzProtocolEncoderSMC5326* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_smc5326_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSMC5326* instance = malloc(sizeof(SubGhzProtocolDecoderSMC5326)); + instance->base.protocol = &subghz_protocol_smc5326; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_smc5326_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + free(instance); +} + +void subghz_protocol_decoder_smc5326_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + instance->decoder.parser_step = SMC5326DecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + + switch(instance->decoder.parser_step) { + case SMC5326DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short * 24) < + subghz_protocol_smc5326_const.te_delta * 12)) { + //Found Preambula + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + } + break; + case SMC5326DecoderStepSaveDuration: + //save duration + if(level) { + instance->decoder.te_last = duration; + instance->te += duration; + instance->decoder.parser_step = SMC5326DecoderStepCheckDuration; + } + break; + case SMC5326DecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_smc5326_const.te_long * 2)) { + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + if(instance->decoder.decode_count_bit == + subghz_protocol_smc5326_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->te /= (instance->decoder.decode_count_bit * 4 + 1); + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->last_data = instance->decoder.decode_data; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + break; + } + + instance->te += duration; + + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_short) < + subghz_protocol_smc5326_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_long) < + subghz_protocol_smc5326_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_long) < + subghz_protocol_smc5326_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short) < + subghz_protocol_smc5326_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = SMC5326DecoderStepReset; + } + } else { + instance->decoder.parser_step = SMC5326DecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_smc5326_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_smc5326_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + res = true; + } while(false); + + return res; +} + +static void subghz_protocol_smc5326_get_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s\r\n", + (((event >> 6) & 0x3) == 0x3 ? "B1 " : ""), + (((event >> 4) & 0x3) == 0x3 ? "B2 " : ""), + (((event >> 2) & 0x3) == 0x3 ? "B3 " : ""), + (((event >> 0) & 0x3) == 0x3 ? "B4 " : "")); +} + +void subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + uint32_t data = (uint32_t)((instance->generic.data >> 9) & 0xFFFF); + + furi_string_cat_printf( + output, + "%s %ubit\r\n" + "Key:%07lX Te:%luus\r\n" + " +: " DIP_PATTERN "\r\n" + " o: " DIP_PATTERN " ", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0x1FFFFFF), + instance->te, + SHOW_DIP_P(data, DIP_P), + SHOW_DIP_P(data, DIP_O)); + subghz_protocol_smc5326_get_event_serialize(instance->generic.data >> 1, output); + furi_string_cat_printf(output, " -: " DIP_PATTERN "\r\n", SHOW_DIP_P(data, DIP_N)); +} diff --git a/applications/main/subghz/protocols/smc5326.h b/applications/main/subghz/protocols/smc5326.h new file mode 100644 index 000000000..ddc954bd5 --- /dev/null +++ b/applications/main/subghz/protocols/smc5326.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_SMC5326_NAME "SMC5326" + +typedef struct SubGhzProtocolDecoderSMC5326 SubGhzProtocolDecoderSMC5326; +typedef struct SubGhzProtocolEncoderSMC5326 SubGhzProtocolEncoderSMC5326; + +extern const SubGhzProtocolDecoder subghz_protocol_smc5326_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_smc5326_encoder; +extern const SubGhzProtocol subghz_protocol_smc5326; + +/** + * Allocate SubGhzProtocolEncoderSMC5326. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSMC5326* pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +void* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSMC5326. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +void subghz_protocol_encoder_smc5326_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +void subghz_protocol_encoder_smc5326_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_smc5326_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderSMC5326. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSMC5326* pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void* subghz_protocol_decoder_smc5326_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void subghz_protocol_decoder_smc5326_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void subghz_protocol_decoder_smc5326_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_smc5326_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/somfy_keytis.c b/applications/main/subghz/protocols/somfy_keytis.c new file mode 100644 index 000000000..ab9202cc3 --- /dev/null +++ b/applications/main/subghz/protocols/somfy_keytis.c @@ -0,0 +1,797 @@ +#include "somfy_keytis.h" +#include + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolSomfyKeytis" + +static const SubGhzBlockConst subghz_protocol_somfy_keytis_const = { + .te_short = 640, + .te_long = 1280, + .te_delta = 250, + .min_count_bit_for_found = 80, +}; + +struct SubGhzProtocolDecoderSomfyKeytis { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + ManchesterState manchester_saved_state; + uint32_t press_duration_counter; +}; + +struct SubGhzProtocolEncoderSomfyKeytis { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + SomfyKeytisDecoderStepReset = 0, + SomfyKeytisDecoderStepCheckPreambula, + SomfyKeytisDecoderStepFoundPreambula, + SomfyKeytisDecoderStepStartDecode, + SomfyKeytisDecoderStepDecoderData, +} SomfyKeytisDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder = { + .alloc = subghz_protocol_decoder_somfy_keytis_alloc, + .free = subghz_protocol_decoder_somfy_keytis_free, + + .feed = subghz_protocol_decoder_somfy_keytis_feed, + .reset = subghz_protocol_decoder_somfy_keytis_reset, + + .get_hash_data = subghz_protocol_decoder_somfy_keytis_get_hash_data, + .serialize = subghz_protocol_decoder_somfy_keytis_serialize, + .deserialize = subghz_protocol_decoder_somfy_keytis_deserialize, + .get_string = subghz_protocol_decoder_somfy_keytis_get_string, +}; + +const SubGhzProtocol subghz_protocol_somfy_keytis = { + .name = SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_somfy_keytis_decoder, + .encoder = &subghz_protocol_somfy_keytis_encoder, +}; + +const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder = { + .alloc = subghz_protocol_encoder_somfy_keytis_alloc, + .free = subghz_protocol_encoder_somfy_keytis_free, + + .deserialize = subghz_protocol_encoder_somfy_keytis_deserialize, + .stop = subghz_protocol_encoder_somfy_keytis_stop, + .yield = subghz_protocol_encoder_somfy_keytis_yield, +}; + +void* subghz_protocol_encoder_somfy_keytis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyKeytis)); + + instance->base.protocol = &subghz_protocol_somfy_keytis; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyKeytis)); + instance->base.protocol = &subghz_protocol_somfy_keytis; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_encoder_somfy_keytis_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + free(instance->encoder.upload); + free(instance); +} + +void subghz_protocol_decoder_somfy_keytis_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + free(instance); +} + +void subghz_protocol_decoder_somfy_keytis_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + instance->decoder.parser_step = SomfyKeytisDecoderStepReset; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +static bool + subghz_protocol_somfy_keytis_gen_data(SubGhzProtocolEncoderSomfyKeytis* instance, uint8_t btn) { + UNUSED(btn); + uint64_t data = instance->generic.data ^ (instance->generic.data >> 8); + instance->generic.btn = (data >> 48) & 0xF; + instance->generic.cnt = (data >> 24) & 0xFFFF; + instance->generic.serial = data & 0xFFFFFF; + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + uint8_t frame[10]; + frame[0] = (0xA << 4) | instance->generic.btn; + frame[1] = 0xF << 4; + frame[2] = instance->generic.cnt >> 8; + frame[3] = instance->generic.cnt; + frame[4] = instance->generic.serial >> 16; + frame[5] = instance->generic.serial >> 8; + frame[6] = instance->generic.serial; + frame[7] = 0xC4; + frame[8] = 0x00; + frame[9] = 0x19; + + uint8_t checksum = 0; + for(uint8_t i = 0; i < 7; i++) { + checksum = checksum ^ frame[i] ^ (frame[i] >> 4); + } + checksum &= 0xF; + + frame[1] |= checksum; + + for(uint8_t i = 1; i < 7; i++) { + frame[i] ^= frame[i - 1]; + } + data = 0; + for(uint8_t i = 0; i < 7; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data = data; + data = 0; + for(uint8_t i = 7; i < 10; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data_2 = data; + return true; +} + +bool subghz_protocol_somfy_keytis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 80; + bool res = subghz_protocol_somfy_keytis_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_somfy_keytis_get_upload( + SubGhzProtocolEncoderSomfyKeytis* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_somfy_keytis_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + //Send header + //Wake up + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)9415); // 1 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)89565); // 0 + //Hardware sync + for(uint8_t i = 0; i < 12; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + for(uint8_t i = 24; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + } else { + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3); + } + + for(uint8_t i = 0; i < 2; ++i) { + //Hardware sync + for(uint8_t i = 0; i < 6; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + for(uint8_t i = 24; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3); + } + } + + //Inter-frame silence + instance->encoder.upload[index - 1].duration += + (uint32_t)30415 - (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + + size_t size_upload = index; + + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + return true; +} + +bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_somfy_keytis_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_somfy_keytis_stop(void* context) { + SubGhzProtocolEncoderSomfyKeytis* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_somfy_keytis_yield(void* context) { + SubGhzProtocolEncoderSomfyKeytis* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +/** + * Сhecksum calculation. + * @param data Вata for checksum calculation + * @return CRC + */ +static uint8_t subghz_protocol_somfy_keytis_crc(uint64_t data) { + uint8_t crc = 0; + data &= 0xFFF0FFFFFFFFFF; + for(uint8_t i = 0; i < 56; i += 8) { + crc = crc ^ data >> i ^ (data >> (i + 4)); + } + return crc & 0xf; +} + +void subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + + ManchesterEvent event = ManchesterEventReset; + switch(instance->decoder.parser_step) { + case SomfyKeytisDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) < + subghz_protocol_somfy_keytis_const.te_delta * 4) { + instance->decoder.parser_step = SomfyKeytisDecoderStepFoundPreambula; + instance->header_count++; + } + break; + case SomfyKeytisDecoderStepFoundPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) < + subghz_protocol_somfy_keytis_const.te_delta * 4)) { + instance->decoder.parser_step = SomfyKeytisDecoderStepCheckPreambula; + } else { + instance->header_count = 0; + instance->decoder.parser_step = SomfyKeytisDecoderStepReset; + } + break; + case SomfyKeytisDecoderStepCheckPreambula: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) < + subghz_protocol_somfy_keytis_const.te_delta * 4) { + instance->decoder.parser_step = SomfyKeytisDecoderStepFoundPreambula; + instance->header_count++; + } else if( + (instance->header_count > 1) && + (DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 7) < + subghz_protocol_somfy_keytis_const.te_delta * 4)) { + instance->decoder.parser_step = SomfyKeytisDecoderStepDecoderData; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->press_duration_counter = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + } + } + + break; + + case SomfyKeytisDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short) < + subghz_protocol_somfy_keytis_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_long) < + subghz_protocol_somfy_keytis_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= (subghz_protocol_somfy_keytis_const.te_long + + subghz_protocol_somfy_keytis_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_somfy_keytis_const.min_count_bit_for_found) { + //check crc + uint64_t data_tmp = instance->generic.data ^ (instance->generic.data >> 8); + if(((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_keytis_crc(data_tmp)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + instance->decoder.parser_step = SomfyKeytisDecoderStepReset; + } else { + instance->decoder.parser_step = SomfyKeytisDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short) < + subghz_protocol_somfy_keytis_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_long) < + subghz_protocol_somfy_keytis_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = SomfyKeytisDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + if(instance->decoder.decode_count_bit < 56) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; + } else { + instance->press_duration_counter = (instance->press_duration_counter << 1) | + data; + } + + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_somfy_keytis_check_remote_controller(SubGhzBlockGeneric* instance) { + //https://pushstack.wordpress.com/somfy-rts-protocol/ + /* + * 604 us + * / + * | 2416us | 2416us | 2416us | 2416us | 4550 us | | + * + * +--------+ +--------+ +---...---+ + * + +--------+ +--------+ +--+XXXX...XXX+ + * + * | hw. sync. | soft. | | + * | | sync. | data | + * + * + * encrypt | decrypt + * + * package 80 bit pdc key btn crc cnt serial + * + * 0xA453537C4B9855 C40019 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 C80026 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 CC0033 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 D00049 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 D4005C => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 D80063 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 DC0076 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 E00086 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 E40093 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 E800AC => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 EC00B9 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 F000C3 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 F400D6 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 F800E9 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC00FC => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC0102 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC0113 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC0120 => 0xA 4 F 7 002F 37D3CD + * .......... + * 0xA453537C4B9855 FC048F => 0xA 4 F 7 002F 37D3CD + * + * Pdc: "Press Duration Counter" the total delay of the button is sent 72 parcels, + * pdc cnt4b cnt8b pdc_crc + * C40019 => 11 0001 00 0000 00000001 1001 + * C80026 => 11 0010 00 0000 00000010 0110 + * CC0033 => 11 0011 00 0000 00000011 0011 + * D00049 => 11 0100 00 0000 00000100 1001 + * D4005C => 11 0101 00 0000 00000101 1100 + * D80063 => 11 0110 00 0000 00000110 0011 + * DC0076 => 11 0111 00 0000 00000111 0110 + * E00086 => 11 1000 00 0000 00001000 0110 + * E40093 => 11 1001 00 0000 00001001 0011 + * E800AC => 11 1010 00 0000 00001010 1100 + * EC00B9 => 11 1011 00 0000 00001011 1001 + * F000C3 => 11 1100 00 0000 00001100 0011 + * F400D6 => 11 1101 00 0000 00001101 0110 + * F800E9 => 11 1110 00 0000 00001110 1001 + * FC00FC => 11 1111 00 0000 00001111 1100 + * FC0102 => 11 1111 00 0000 00010000 0010 + * FC0113 => 11 1111 00 0000 00010001 0011 + * FC0120 => 11 1111 00 0000 00010010 0000 + * + * Cnt4b: 4-bit counter changes from 1 to 15 then always equals 15 + * Cnt8b: 8-bit counter changes from 1 to 72 (0x48) + * Ppdc_crc: + * uint8_t crc=0; + * for(i=4; i<24; i+=4){ + * crc ^=(pdc>>i); + * } + * return crc; + * example: crc = 1^0^0^4^C = 9 + * 11, 00, 0000: const + * + * Key: “Encryption Key”, Most significant 4-bit are always 0xA, Least Significant bits is + * a linear counter. In the Smoove Origin this counter is increased together with the + * rolling code. But leaving this on a constant value also works. Gerardwr notes that + * for some other types of remotes the MSB is not constant. + * Btn: 4-bit Control codes, this indicates the button that is pressed + * CRC: 4-bit Checksum. + * Ctn: 16-bit rolling code (big-endian) increased with every button press. + * Serial: 24-bit identifier of sending device (little-endian) + * + * + * Decrypt + * + * uint8_t frame[7]; + * for (i=1; i < 7; i++) { + * frame[i] = frame[i] ^ frame[i-1]; + * } + * or + * uint64 Decrypt = frame ^ (frame>>8); + * + * CRC + * + * uint8_t frame[7]; + * for (i=0; i < 7; i++) { + * crc = crc ^ frame[i] ^ (frame[i] >> 4); + * } + * crc = crc & 0xf; + * + */ + + uint64_t data = instance->data ^ (instance->data >> 8); + instance->btn = (data >> 48) & 0xF; + instance->cnt = (data >> 24) & 0xFFFF; + instance->serial = data & 0xFFFFFF; +} + +/** + * Get button name. + * @param btn Button number, 4 bit + */ +static const char* subghz_protocol_somfy_keytis_get_name_button(uint8_t btn) { + const char* name_btn[0x10] = { + "Unknown", + "0x01", + "0x02", + "Prog", + "Key_1", + "0x05", + "0x06", + "0x07", + "0x08", + "0x09", + "0x0A", + "0x0B", + "0x0C", + "0x0D", + "0x0E", + "0x0F"}; + return btn <= 0xf ? name_btn[btn] : name_btn[0]; +} + +uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_somfy_keytis_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32( + flipper_format, "Duration_Counter", &instance->press_duration_counter, 1)) { + FURI_LOG_E(TAG, "Unable to add Duration_Counter"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_somfy_keytis_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32( + flipper_format, + "Duration_Counter", + (uint32_t*)&instance->press_duration_counter, + 1)) { + FURI_LOG_E(TAG, "Missing Duration_Counter"); + break; + } + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + + subghz_protocol_somfy_keytis_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "%lX%08lX%06lX\r\n" + "Sn:0x%06lX \r\n" + "Cnt:0x%04lX\r\n" + "Btn:%s\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->press_duration_counter, + instance->generic.serial, + instance->generic.cnt, + subghz_protocol_somfy_keytis_get_name_button(instance->generic.btn)); +} diff --git a/applications/main/subghz/protocols/somfy_keytis.h b/applications/main/subghz/protocols/somfy_keytis.h new file mode 100644 index 000000000..b08da3641 --- /dev/null +++ b/applications/main/subghz/protocols/somfy_keytis.h @@ -0,0 +1,125 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME "Somfy Keytis" + +typedef struct SubGhzProtocolDecoderSomfyKeytis SubGhzProtocolDecoderSomfyKeytis; +typedef struct SubGhzProtocolEncoderSomfyKeytis SubGhzProtocolEncoderSomfyKeytis; + +extern const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder; +extern const SubGhzProtocol subghz_protocol_somfy_keytis; + +/** + * Allocate SubGhzProtocolEncoderSomfyKeytis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSomfyKeytis* pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void* subghz_protocol_encoder_somfy_keytis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void subghz_protocol_encoder_somfy_keytis_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_somfy_keytis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void subghz_protocol_encoder_somfy_keytis_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_somfy_keytis_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderSomfyKeytis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSomfyKeytis* pointer to a SubGhzProtocolDecoderSomfyKeytis instance + */ +void* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + */ +void subghz_protocol_decoder_somfy_keytis_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + */ +void subghz_protocol_decoder_somfy_keytis_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_somfy_keytis_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + * @param output Resulting text + */ +void subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/somfy_telis.c b/applications/main/subghz/protocols/somfy_telis.c new file mode 100644 index 000000000..96997c581 --- /dev/null +++ b/applications/main/subghz/protocols/somfy_telis.c @@ -0,0 +1,663 @@ +#include "somfy_telis.h" +#include + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolSomfyTelis" + +static const SubGhzBlockConst subghz_protocol_somfy_telis_const = { + .te_short = 640, + .te_long = 1280, + .te_delta = 250, + .min_count_bit_for_found = 56, +}; + +struct SubGhzProtocolDecoderSomfyTelis { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + ManchesterState manchester_saved_state; +}; + +struct SubGhzProtocolEncoderSomfyTelis { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + SomfyTelisDecoderStepReset = 0, + SomfyTelisDecoderStepCheckPreambula, + SomfyTelisDecoderStepFoundPreambula, + SomfyTelisDecoderStepStartDecode, + SomfyTelisDecoderStepDecoderData, +} SomfyTelisDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder = { + .alloc = subghz_protocol_decoder_somfy_telis_alloc, + .free = subghz_protocol_decoder_somfy_telis_free, + + .feed = subghz_protocol_decoder_somfy_telis_feed, + .reset = subghz_protocol_decoder_somfy_telis_reset, + + .get_hash_data = subghz_protocol_decoder_somfy_telis_get_hash_data, + .serialize = subghz_protocol_decoder_somfy_telis_serialize, + .deserialize = subghz_protocol_decoder_somfy_telis_deserialize, + .get_string = subghz_protocol_decoder_somfy_telis_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder = { + .alloc = subghz_protocol_encoder_somfy_telis_alloc, + .free = subghz_protocol_encoder_somfy_telis_free, + + .deserialize = subghz_protocol_encoder_somfy_telis_deserialize, + .stop = subghz_protocol_encoder_somfy_telis_stop, + .yield = subghz_protocol_encoder_somfy_telis_yield, +}; + +const SubGhzProtocol subghz_protocol_somfy_telis = { + .name = SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_somfy_telis_decoder, + .encoder = &subghz_protocol_somfy_telis_encoder, +}; + +void* subghz_protocol_encoder_somfy_telis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyTelis)); + + instance->base.protocol = &subghz_protocol_somfy_telis; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void subghz_protocol_encoder_somfy_telis_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static bool + subghz_protocol_somfy_telis_gen_data(SubGhzProtocolEncoderSomfyTelis* instance, uint8_t btn) { + UNUSED(btn); + uint64_t data = instance->generic.data ^ (instance->generic.data >> 8); + instance->generic.btn = (data >> 44) & 0xF; // ctrl + instance->generic.cnt = (data >> 24) & 0xFFFF; // rolling code + instance->generic.serial = data & 0xFFFFFF; // address + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + uint8_t frame[7]; + frame[0] = data >> 48; + frame[1] = instance->generic.btn << 4; + frame[2] = instance->generic.cnt >> 8; + frame[3] = instance->generic.cnt; + frame[4] = instance->generic.serial >> 16; + frame[5] = instance->generic.serial >> 8; + frame[6] = instance->generic.serial; + + uint8_t checksum = 0; + for(uint8_t i = 0; i < 7; i++) { + checksum = checksum ^ frame[i] ^ (frame[i] >> 4); + } + checksum &= 0xF; + + frame[1] |= checksum; + + for(uint8_t i = 1; i < 7; i++) { + frame[i] ^= frame[i - 1]; + } + data = 0; + for(uint8_t i = 0; i < 7; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data = data; + return true; +} + +bool subghz_protocol_somfy_telis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 56; + bool res = subghz_protocol_somfy_telis_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_somfy_telis_get_upload( + SubGhzProtocolEncoderSomfyTelis* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_somfy_telis_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + //Send header + //Wake up + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)9415); // 1 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)89565); // 0 + //Hardware sync + for(uint8_t i = 0; i < 2; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += (uint32_t)30415; + } else { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)30415); + } + + //Retransmission + for(uint8_t i = 0; i < 2; i++) { + //Hardware sync + for(uint8_t i = 0; i < 7; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += (uint32_t)30415; + } else { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)30415); + } + } + + size_t size_upload = index; + + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + return true; +} + +bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_somfy_telis_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_somfy_telis_stop(void* context) { + SubGhzProtocolEncoderSomfyTelis* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_somfy_telis_yield(void* context) { + SubGhzProtocolEncoderSomfyTelis* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_somfy_telis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyTelis)); + instance->base.protocol = &subghz_protocol_somfy_telis; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_somfy_telis_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + free(instance); +} + +void subghz_protocol_decoder_somfy_telis_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + instance->decoder.parser_step = SomfyTelisDecoderStepReset; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +/** + * Сhecksum calculation. + * @param data Вata for checksum calculation + * @return CRC + */ +static uint8_t subghz_protocol_somfy_telis_crc(uint64_t data) { + uint8_t crc = 0; + data &= 0xFFF0FFFFFFFFFF; + for(uint8_t i = 0; i < 56; i += 8) { + crc = crc ^ data >> i ^ (data >> (i + 4)); + } + return crc & 0xf; +} + +void subghz_protocol_decoder_somfy_telis_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + + ManchesterEvent event = ManchesterEventReset; + switch(instance->decoder.parser_step) { + case SomfyTelisDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) < + subghz_protocol_somfy_telis_const.te_delta * 4) { + instance->decoder.parser_step = SomfyTelisDecoderStepFoundPreambula; + instance->header_count++; + } + break; + case SomfyTelisDecoderStepFoundPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) < + subghz_protocol_somfy_telis_const.te_delta * 4)) { + instance->decoder.parser_step = SomfyTelisDecoderStepCheckPreambula; + } else { + instance->header_count = 0; + instance->decoder.parser_step = SomfyTelisDecoderStepReset; + } + break; + case SomfyTelisDecoderStepCheckPreambula: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) < + subghz_protocol_somfy_telis_const.te_delta * 4) { + instance->decoder.parser_step = SomfyTelisDecoderStepFoundPreambula; + instance->header_count++; + } else if( + (instance->header_count > 1) && + (DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 7) < + subghz_protocol_somfy_telis_const.te_delta * 4)) { + instance->decoder.parser_step = SomfyTelisDecoderStepDecoderData; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + } + } + + break; + + case SomfyTelisDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short) < + subghz_protocol_somfy_telis_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_long) < + subghz_protocol_somfy_telis_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= (subghz_protocol_somfy_telis_const.te_long + + subghz_protocol_somfy_telis_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_somfy_telis_const.min_count_bit_for_found) { + //check crc + uint64_t data_tmp = instance->decoder.decode_data ^ + (instance->decoder.decode_data >> 8); + if(((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_telis_crc(data_tmp)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + instance->decoder.parser_step = SomfyTelisDecoderStepReset; + } else { + instance->decoder.parser_step = SomfyTelisDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short) < + subghz_protocol_somfy_telis_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_long) < + subghz_protocol_somfy_telis_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = SomfyTelisDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { + //https://pushstack.wordpress.com/somfy-rts-protocol/ + /* + * 604 us + * / + * | 2416us | 2416us | 2416us | 2416us | 4550 us | | 67648 us | 30415 us | + * + * +--------+ +--------+ +---...---+ + * + +--------+ +--------+ +--+XXXX...XXX+-----...----- + * + * | hw. sync. | soft. | | Inter-frame + * | | sync. | data | gap + * + * + * encrypt | decrypt + * + * package 56 bit cnt key btn|crc cnt serial + * 0xA7232323312222 - 0 => A7 8 0 | 00 00 | 12 13 00 + * 0xA7222223312222 - 1 => A7 8 5 | 00 01 | 12 13 00 + * 0xA7212123312222 - 2 => A7 8 6 | 00 02 | 12 13 00 + * + * Key: “Encryption Key”, Most significant 4-bit are always 0xA, Least Significant bits is + * a linear counter. In the Smoove Origin this counter is increased together with the + * rolling code. But leaving this on a constant value also works. Gerardwr notes that + * for some other types of remotes the MSB is not constant. + * Btn: 4-bit Control codes, this indicates the button that is pressed + * CRC: 4-bit Checksum. + * Ctn: 16-bit rolling code (big-endian) increased with every button press. + * Serial: 24-bit identifier of sending device (little-endian) + * + * + * Decrypt + * + * uint8_t frame[7]; + * for (i=1; i < 7; i++) { + * frame[i] = frame[i] ^ frame[i-1]; + * } + * or + * uint64 Decrypt = frame ^ (frame>>8); + * + * Btn + * + * Value Button(s) Description + * 0x1 My Stop or move to favourite position + * 0x2 Up Move up + * 0x3 My + Up Set upper motor limit in initial programming mode + * 0x4 Down Move down + * 0x5 My + Down Set lower motor limit in initial programming mode + * 0x6 Up + Down Change motor limit and initial programming mode + * 0x8 Prog Used for (de-)registering remotes, see below + * 0x9 Sun + Flag Enable sun and wind detector (SUN and FLAG symbol on the Telis Soliris RC) + * 0xA Flag Disable sun detector (FLAG symbol on the Telis Soliris RC) + * + * CRC + * + * uint8_t frame[7]; + * for (i=0; i < 7; i++) { + * cksum = cksum ^ frame[i] ^ (frame[i] >> 4); + * } + * cksum = cksum & 0xf; + * + */ + + uint64_t data = instance->data ^ (instance->data >> 8); + instance->btn = (data >> 44) & 0xF; // ctrl + instance->cnt = (data >> 24) & 0xFFFF; // rolling code + instance->serial = data & 0xFFFFFF; // address +} + +/** + * Get button name. + * @param btn Button number, 4 bit + */ +static const char* subghz_protocol_somfy_telis_get_name_button(uint8_t btn) { + const char* name_btn[16] = { + "Unknown", + "My", + "Up", + "My+Up", + "Down", + "My+Down", + "Up+Down", + "0x07", + "Prog", + "Sun+Flag", + "Flag", + "0x0B", + "0x0C", + "0x0D", + "0x0E", + "0x0F"}; + return btn <= 0xf ? name_btn[btn] : name_btn[0]; +} + +uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_somfy_telis_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_somfy_telis_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + + subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%06lX \r\n" + "Cnt:0x%04lX\r\n" + "Btn:%s\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->generic.serial, + instance->generic.cnt, + subghz_protocol_somfy_telis_get_name_button(instance->generic.btn)); +} diff --git a/applications/main/subghz/protocols/somfy_telis.h b/applications/main/subghz/protocols/somfy_telis.h new file mode 100644 index 000000000..b5e989866 --- /dev/null +++ b/applications/main/subghz/protocols/somfy_telis.h @@ -0,0 +1,125 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME "Somfy Telis" + +typedef struct SubGhzProtocolDecoderSomfyTelis SubGhzProtocolDecoderSomfyTelis; +typedef struct SubGhzProtocolEncoderSomfyTelis SubGhzProtocolEncoderSomfyTelis; + +extern const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder; +extern const SubGhzProtocol subghz_protocol_somfy_telis; + +/** + * Allocate SubGhzProtocolEncoderSomfyTelis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSomfyTelis* pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void* subghz_protocol_encoder_somfy_telis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void subghz_protocol_encoder_somfy_telis_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_somfy_telis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void subghz_protocol_encoder_somfy_telis_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_somfy_telis_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderSomfyTelis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSomfyTelis* pointer to a SubGhzProtocolDecoderSomfyTelis instance + */ +void* subghz_protocol_decoder_somfy_telis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + */ +void subghz_protocol_decoder_somfy_telis_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + */ +void subghz_protocol_decoder_somfy_telis_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_somfy_telis_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_somfy_telis_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + * @param output Resulting text + */ +void subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/star_line.c b/applications/main/subghz/protocols/star_line.c new file mode 100644 index 000000000..3066c6e2b --- /dev/null +++ b/applications/main/subghz/protocols/star_line.c @@ -0,0 +1,780 @@ +#include "star_line.h" +#include "keeloq_common.h" + +#include "../subghz_keystore.h" +#include + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolStarLine" + +static const SubGhzBlockConst subghz_protocol_star_line_const = { + .te_short = 250, + .te_long = 500, + .te_delta = 120, + .min_count_bit_for_found = 64, +}; + +struct SubGhzProtocolDecoderStarLine { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + SubGhzKeystore* keystore; + const char* manufacture_name; + + FuriString* manufacture_from_file; +}; + +struct SubGhzProtocolEncoderStarLine { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + SubGhzKeystore* keystore; + const char* manufacture_name; + + FuriString* manufacture_from_file; +}; + +typedef enum { + StarLineDecoderStepReset = 0, + StarLineDecoderStepCheckPreambula, + StarLineDecoderStepSaveDuration, + StarLineDecoderStepCheckDuration, +} StarLineDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_star_line_decoder = { + .alloc = subghz_protocol_decoder_star_line_alloc, + .free = subghz_protocol_decoder_star_line_free, + + .feed = subghz_protocol_decoder_star_line_feed, + .reset = subghz_protocol_decoder_star_line_reset, + + .get_hash_data = subghz_protocol_decoder_star_line_get_hash_data, + .serialize = subghz_protocol_decoder_star_line_serialize, + .deserialize = subghz_protocol_decoder_star_line_deserialize, + .get_string = subghz_protocol_decoder_star_line_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_star_line_encoder = { + .alloc = subghz_protocol_encoder_star_line_alloc, + .free = subghz_protocol_encoder_star_line_free, + + .deserialize = subghz_protocol_encoder_star_line_deserialize, + .stop = subghz_protocol_encoder_star_line_stop, + .yield = subghz_protocol_encoder_star_line_yield, +}; + +const SubGhzProtocol subghz_protocol_star_line = { + .name = SUBGHZ_PROTOCOL_STAR_LINE_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_star_line_decoder, + .encoder = &subghz_protocol_star_line_encoder, +}; + +static const char* mfname; + +static int kl_type; + +void star_line_reset_mfname() { + mfname = ""; +} + +void star_line_reset_kl_type() { + kl_type = 0; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + */ +static void subghz_protocol_star_line_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name); + +void* subghz_protocol_encoder_star_line_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderStarLine* instance = malloc(sizeof(SubGhzProtocolEncoderStarLine)); + + instance->base.protocol = &subghz_protocol_star_line; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + + instance->manufacture_from_file = furi_string_alloc(); + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void subghz_protocol_encoder_star_line_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderStarLine* instance = context; + furi_string_free(instance->manufacture_from_file); + free(instance->encoder.upload); + free(instance); +} + +/** + * Key generation from simple data + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq* instance + * @param btn Button number, 4 bit + */ +static bool + subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) { + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + uint32_t fix = btn << 24 | instance->generic.serial; + uint32_t decrypt = btn << 24 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; + uint32_t hop = 0; + uint64_t man = 0; + uint64_t code_found_reverse; + int res = 0; + + if(instance->manufacture_name == 0x0) { + instance->manufacture_name = ""; + } + + if(strcmp(instance->manufacture_name, "Unknown") == 0) { + code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + hop = code_found_reverse & 0x00000000ffffffff; + } else { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + //Simple Learning + hop = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); + break; + case KEELOQ_LEARNING_NORMAL: + //Normal Learning + man = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_UNKNOWN: + if(kl_type == 1) { + hop = + subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); + } + if(kl_type == 2) { + man = subghz_protocol_keeloq_common_normal_learning( + fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + break; + } + break; + } + } + } + if(hop) { + uint64_t yek = (uint64_t)fix << 32 | hop; + instance->generic.data = + subghz_protocol_blocks_reverse_key(yek, instance->generic.data_count_bit); + return true; + } else { + instance->manufacture_name = "Unknown"; + return false; + } +} + +bool subghz_protocol_star_line_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + const char* manufacture_name, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderStarLine* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->manufacture_name = manufacture_name; + instance->generic.data_count_bit = 64; + bool res = subghz_protocol_star_line_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_star_line_get_upload( + SubGhzProtocolEncoderStarLine* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_star_line_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + size_t size_upload = 6 * 2 + (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + for(uint8_t i = 6; i > 0; i--) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_star_line_const.te_long * 2); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_star_line_const.te_long * 2); + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_star_line_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_star_line_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_star_line_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_star_line_const.te_short); + } + } + + return true; +} + +bool subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderStarLine* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + // Read manufacturer from file + if(flipper_format_read_string( + flipper_format, "Manufacture", instance->manufacture_from_file)) { + instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); + mfname = furi_string_get_cstr(instance->manufacture_from_file); + } else { + FURI_LOG_D(TAG, "ENCODER: Missing Manufacture"); + } + + subghz_protocol_star_line_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_star_line_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_star_line_stop(void* context) { + SubGhzProtocolEncoderStarLine* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_star_line_yield(void* context) { + SubGhzProtocolEncoderStarLine* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_star_line_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderStarLine* instance = malloc(sizeof(SubGhzProtocolDecoderStarLine)); + instance->base.protocol = &subghz_protocol_star_line; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->manufacture_from_file = furi_string_alloc(); + + instance->keystore = subghz_environment_get_keystore(environment); + + return instance; +} + +void subghz_protocol_decoder_star_line_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + furi_string_free(instance->manufacture_from_file); + + free(instance); +} + +void subghz_protocol_decoder_star_line_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + instance->decoder.parser_step = StarLineDecoderStepReset; + mfname = ""; + kl_type = 0; +} + +void subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + + switch(instance->decoder.parser_step) { + case StarLineDecoderStepReset: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long * 2) < + subghz_protocol_star_line_const.te_delta * 2) { + instance->decoder.parser_step = StarLineDecoderStepCheckPreambula; + instance->header_count++; + } else if(instance->header_count > 4) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.te_last = duration; + instance->decoder.parser_step = StarLineDecoderStepCheckDuration; + } + } else { + instance->header_count = 0; + } + break; + case StarLineDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long * 2) < + subghz_protocol_star_line_const.te_delta * 2)) { + //Found Preambula + instance->decoder.parser_step = StarLineDecoderStepReset; + } else { + instance->header_count = 0; + instance->decoder.parser_step = StarLineDecoderStepReset; + } + break; + case StarLineDecoderStepSaveDuration: + if(level) { + if(duration >= (subghz_protocol_star_line_const.te_long + + subghz_protocol_star_line_const.te_delta)) { + instance->decoder.parser_step = StarLineDecoderStepReset; + if(instance->decoder.decode_count_bit >= + subghz_protocol_star_line_const.min_count_bit_for_found) { + if(instance->generic.data != instance->decoder.decode_data) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = StarLineDecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = StarLineDecoderStepReset; + } + break; + case StarLineDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_star_line_const.te_short) < + subghz_protocol_star_line_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_short) < + subghz_protocol_star_line_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = StarLineDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_star_line_const.te_long) < + subghz_protocol_star_line_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long) < + subghz_protocol_star_line_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = StarLineDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = StarLineDecoderStepReset; + } + } else { + instance->decoder.parser_step = StarLineDecoderStepReset; + } + break; + } +} + +/** + * Validation of decrypt data. + * @param instance Pointer to a SubGhzBlockGeneric instance + * @param decrypt Decrypd data + * @param btn Button number, 4 bit + * @param end_serial decrement the last 10 bits of the serial number + * @return true On success + */ +static inline bool subghz_protocol_star_line_check_decrypt( + SubGhzBlockGeneric* instance, + uint32_t decrypt, + uint8_t btn, + uint32_t end_serial) { + furi_assert(instance); + if((decrypt >> 24 == btn) && ((((uint16_t)(decrypt >> 16)) & 0x00FF) == end_serial)) { + instance->cnt = decrypt & 0x0000FFFF; + return true; + } + return false; +} + +/** + * Checking the accepted code against the database manafacture key + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param fix Fix part of the parcel + * @param hop Hop encrypted part of the parcel + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + * @return true on successful search + */ +static uint8_t subghz_protocol_star_line_check_remote_controller_selector( + SubGhzBlockGeneric* instance, + uint32_t fix, + uint32_t hop, + SubGhzKeystore* keystore, + const char** manufacture_name) { + uint16_t end_serial = (uint16_t)(fix & 0xFF); + uint8_t btn = (uint8_t)(fix >> 24); + uint32_t decrypt = 0; + uint64_t man_normal_learning; + int res = 0; + if(mfname == 0x0) { + mfname = ""; + } + + if(strcmp(mfname, "") == 0) { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_NORMAL: + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_UNKNOWN: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + // Check for mirrored man + uint64_t man_rev = 0; + uint64_t man_rev_byte = 0; + for(uint8_t i = 0; i < 64; i += 8) { + man_rev_byte = (uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56 - i); + } + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + //########################### + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + // Check for mirrored man + man_normal_learning = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + break; + } + } + } else if(strcmp(mfname, "Unknown") == 0) { + return 1; + } else { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_NORMAL: + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_UNKNOWN: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + // Check for mirrored man + uint64_t man_rev = 0; + uint64_t man_rev_byte = 0; + for(uint8_t i = 0; i < 64; i += 8) { + man_rev_byte = (uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56 - i); + } + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + //########################### + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Check for mirrored man + man_normal_learning = + subghz_protocol_keeloq_common_normal_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + break; + } + } + } + } + + *manufacture_name = "Unknown"; + mfname = "Unknown"; + instance->cnt = 0; + + return 0; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + */ +static void subghz_protocol_star_line_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name) { + uint64_t key = subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); + uint32_t key_fix = key >> 32; + uint32_t key_hop = key & 0x00000000ffffffff; + + subghz_protocol_star_line_check_remote_controller_selector( + instance, key_fix, key_hop, keystore, manufacture_name); + + instance->serial = key_fix & 0x00FFFFFF; + instance->btn = key_fix >> 24; +} + +uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_star_line_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + subghz_protocol_star_line_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + if(res && !flipper_format_write_string_cstr( + flipper_format, "Manufacture", instance->manufacture_name)) { + FURI_LOG_E(TAG, "Unable to add manufacture name"); + res = false; + } + if(res && instance->generic.data_count_bit != + subghz_protocol_star_line_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + // Read manufacturer from file + if(flipper_format_read_string( + flipper_format, "Manufacture", instance->manufacture_from_file)) { + instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); + mfname = furi_string_get_cstr(instance->manufacture_from_file); + } else { + FURI_LOG_D(TAG, "DECODER: Missing Manufacture"); + } + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_star_line_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + + subghz_protocol_star_line_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + uint32_t code_found_reverse_hi = code_found_reverse >> 32; + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%08lX%08lX\r\n" + "Fix:0x%08lX Cnt:%04lX\r\n" + "Hop:0x%08lX Btn:%02X\r\n" + "MF:%s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + instance->generic.cnt, + code_found_reverse_lo, + instance->generic.btn, + instance->manufacture_name); +} diff --git a/applications/main/subghz/protocols/star_line.h b/applications/main/subghz/protocols/star_line.h new file mode 100644 index 000000000..e8873d41a --- /dev/null +++ b/applications/main/subghz/protocols/star_line.h @@ -0,0 +1,131 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_STAR_LINE_NAME "Star Line" + +typedef struct SubGhzProtocolDecoderStarLine SubGhzProtocolDecoderStarLine; +typedef struct SubGhzProtocolEncoderStarLine SubGhzProtocolEncoderStarLine; + +extern const SubGhzProtocolDecoder subghz_protocol_star_line_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_star_line_encoder; +extern const SubGhzProtocol subghz_protocol_star_line; + +void star_line_reset_mfname(); + +void star_line_reset_kl_type(); + +/** + * Allocate SubGhzProtocolEncoderStarLine. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderStarLine* pointer to a SubGhzProtocolEncoderStarLine instance + */ +void* subghz_protocol_encoder_star_line_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderStarLine. + * @param context Pointer to a SubGhzProtocolEncoderStarLine instance + */ +void subghz_protocol_encoder_star_line_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderStarLine instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_star_line_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + const char* manufacture_name, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderStarLine instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderStarLine instance + */ +void subghz_protocol_encoder_star_line_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderStarLine instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_star_line_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderStarLine. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderStarLine* pointer to a SubGhzProtocolDecoderStarLine instance + */ +void* subghz_protocol_decoder_star_line_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderStarLine. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + */ +void subghz_protocol_decoder_star_line_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderStarLine. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + */ +void subghz_protocol_decoder_star_line_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderStarLine. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_star_line_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderStarLine. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + * @param output Resulting text + */ +void subghz_protocol_decoder_star_line_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/receiver.c b/applications/main/subghz/receiver.c new file mode 100644 index 000000000..698fe098e --- /dev/null +++ b/applications/main/subghz/receiver.c @@ -0,0 +1,124 @@ +#include "receiver.h" + +#include "registry.h" +#include "protocols/protocol_items.h" + +#include + +typedef struct { + SubGhzProtocolEncoderBase* base; +} SubGhzReceiverSlot; + +ARRAY_DEF(SubGhzReceiverSlotArray, SubGhzReceiverSlot, M_POD_OPLIST); +#define M_OPL_SubGhzReceiverSlotArray_t() ARRAY_OPLIST(SubGhzReceiverSlotArray, M_POD_OPLIST) + +struct SubGhzReceiver { + SubGhzReceiverSlotArray_t slots; + SubGhzProtocolFlag filter; + + SubGhzReceiverCallback callback; + void* context; +}; + +SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) { + SubGhzReceiver* instance = malloc(sizeof(SubGhzReceiver)); + SubGhzReceiverSlotArray_init(instance->slots); + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(environment); + + for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry_items); ++i) { + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_index(protocol_registry_items, i); + + if(protocol->decoder && protocol->decoder->alloc) { + SubGhzReceiverSlot* slot = SubGhzReceiverSlotArray_push_new(instance->slots); + slot->base = protocol->decoder->alloc(environment); + } + } + + instance->callback = NULL; + instance->context = NULL; + return instance; +} + +void subghz_receiver_free(SubGhzReceiver* instance) { + furi_assert(instance); + + instance->callback = NULL; + instance->context = NULL; + + // Release allocated slots + for + M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { + slot->base->protocol->decoder->free(slot->base); + slot->base = NULL; + } + SubGhzReceiverSlotArray_clear(instance->slots); + + free(instance); +} + +void subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t duration) { + furi_assert(instance); + furi_assert(instance->slots); + + for + M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { + if((slot->base->protocol->flag & instance->filter) != 0) { + slot->base->protocol->decoder->feed(slot->base, level, duration); + } + } +} + +void subghz_receiver_reset(SubGhzReceiver* instance) { + furi_assert(instance); + furi_assert(instance->slots); + + for + M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { + slot->base->protocol->decoder->reset(slot->base); + } +} + +static void subghz_receiver_rx_callback(SubGhzProtocolDecoderBase* decoder_base, void* context) { + SubGhzReceiver* instance = context; + if(instance->callback) { + instance->callback(instance, decoder_base, instance->context); + } +} + +void subghz_receiver_set_rx_callback( + SubGhzReceiver* instance, + SubGhzReceiverCallback callback, + void* context) { + furi_assert(instance); + + for + M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { + subghz_protocol_decoder_base_set_decoder_callback( + (SubGhzProtocolDecoderBase*)slot->base, subghz_receiver_rx_callback, instance); + } + + instance->callback = callback; + instance->context = context; +} + +void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag filter) { + furi_assert(instance); + instance->filter = filter; +} + +SubGhzProtocolDecoderBase* subghz_receiver_search_decoder_base_by_name( + SubGhzReceiver* instance, + const char* decoder_name) { + SubGhzProtocolDecoderBase* result = NULL; + + for + M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { + if(strcmp(slot->base->protocol->name, decoder_name) == 0) { + result = (SubGhzProtocolDecoderBase*)slot->base; + break; + } + } + return result; +} diff --git a/applications/main/subghz/receiver.h b/applications/main/subghz/receiver.h new file mode 100644 index 000000000..2ef722d1f --- /dev/null +++ b/applications/main/subghz/receiver.h @@ -0,0 +1,73 @@ +#pragma once + +#include "types.h" +#include "protocols/base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzReceiver SubGhzReceiver; + +typedef void (*SubGhzReceiverCallback)( + SubGhzReceiver* decoder, + SubGhzProtocolDecoderBase* decoder_base, + void* context); + +/** + * Allocate and init SubGhzReceiver. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzReceiver* pointer to a SubGhzReceiver instance + */ +SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment); + +/** + * Free SubGhzReceiver. + * @param instance Pointer to a SubGhzReceiver instance + */ +void subghz_receiver_free(SubGhzReceiver* instance); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param instance Pointer to a SubGhzReceiver instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t duration); + +/** + * Reset decoder SubGhzReceiver. + * @param instance Pointer to a SubGhzReceiver instance + */ +void subghz_receiver_reset(SubGhzReceiver* instance); + +/** + * Set a callback upon completion of successful decoding of one of the protocols. + * @param instance Pointer to a SubGhzReceiver instance + * @param callback Callback, SubGhzReceiverCallback + * @param context Context + */ +void subghz_receiver_set_rx_callback( + SubGhzReceiver* instance, + SubGhzReceiverCallback callback, + void* context); + +/** + * Set the filter of receivers that will work at the moment. + * @param instance Pointer to a SubGhzReceiver instance + * @param filter Filter, SubGhzProtocolFlag + */ +void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag filter); + +/** + * Search for a cattery by his name. + * @param instance Pointer to a SubGhzReceiver instance + * @param decoder_name Receiver name + * @return SubGhzProtocolDecoderBase* pointer to a SubGhzProtocolDecoderBase instance + */ +SubGhzProtocolDecoderBase* + subghz_receiver_search_decoder_base_by_name(SubGhzReceiver* instance, const char* decoder_name); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/registry.c b/applications/main/subghz/registry.c new file mode 100644 index 000000000..d0c22ea8c --- /dev/null +++ b/applications/main/subghz/registry.c @@ -0,0 +1,30 @@ +#include "registry.h" + +const SubGhzProtocol* subghz_protocol_registry_get_by_name( + const SubGhzProtocolRegistry* protocol_registry, + const char* name) { + furi_assert(protocol_registry); + + for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry); i++) { + if(strcmp(name, protocol_registry->items[i]->name) == 0) { + return protocol_registry->items[i]; + } + } + return NULL; +} + +const SubGhzProtocol* subghz_protocol_registry_get_by_index( + const SubGhzProtocolRegistry* protocol_registry, + size_t index) { + furi_assert(protocol_registry); + if(index < subghz_protocol_registry_count(protocol_registry)) { + return protocol_registry->items[index]; + } else { + return NULL; + } +} + +size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry) { + furi_assert(protocol_registry); + return protocol_registry->size; +} diff --git a/applications/main/subghz/registry.h b/applications/main/subghz/registry.h new file mode 100644 index 000000000..91027807e --- /dev/null +++ b/applications/main/subghz/registry.h @@ -0,0 +1,47 @@ +#pragma once + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzEnvironment SubGhzEnvironment; + +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; + +struct SubGhzProtocolRegistry { + const SubGhzProtocol** items; + const size_t size; +}; + +/** + * Registration by name SubGhzProtocol. + * @param protocol_registry SubGhzProtocolRegistry + * @param name Protocol name + * @return SubGhzProtocol* pointer to a SubGhzProtocol instance + */ +const SubGhzProtocol* subghz_protocol_registry_get_by_name( + const SubGhzProtocolRegistry* protocol_registry, + const char* name); + +/** + * Registration protocol by index in array SubGhzProtocol. + * @param protocol_registry SubGhzProtocolRegistry + * @param index Protocol by index in array + * @return SubGhzProtocol* pointer to a SubGhzProtocol instance + */ +const SubGhzProtocol* subghz_protocol_registry_get_by_index( + const SubGhzProtocolRegistry* protocol_registry, + size_t index); + +/** + * Getting the number of registered protocols. + * @param protocol_registry SubGhzProtocolRegistry + * @return Number of protocols + */ +size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/scenes/subghz_scene_config.h b/applications/main/subghz/scenes/subghz_scene_config.h index 1ad41a8b5..5acd534dc 100644 --- a/applications/main/subghz/scenes/subghz_scene_config.h +++ b/applications/main/subghz/scenes/subghz_scene_config.h @@ -26,6 +26,7 @@ ADD_SCENE(subghz, set_fix_bft, SetFixBft) ADD_SCENE(subghz, set_cnt_bft, SetCntBft) ADD_SCENE(subghz, set_seed_bft, SetSeedBft) ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer) +ADD_SCENE(subghz, ext_module_settings, ExtModuleSettings) ADD_SCENE(subghz, read_raw, ReadRAW) ADD_SCENE(subghz, more_raw, MoreRAW) ADD_SCENE(subghz, decode_raw, DecodeRAW) diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 6194d0dba..5746efbed 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -170,11 +170,6 @@ void subghz_scene_decode_raw_on_enter(void* context) { subghz_receiver_set_rx_callback( subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); - // make sure we're not in auto-detect mode, which is only meant for the Read app - subghz_protocol_decoder_raw_set_auto_mode( - subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME), - false); subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); if(subghz->decode_raw_state == SubGhzDecodeRawStateStart) { diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 4f00df70b..de17d7a86 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -32,7 +32,6 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); } else if(scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneSaved)) { - //scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); } else { scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c new file mode 100644 index 000000000..7d7a505cb --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c @@ -0,0 +1,92 @@ +#include "../subghz_i.h" +#include "../helpers/subghz_custom_event.h" + +uint8_t value_index; +uint8_t value_index2; + +#define EXT_MODULES_COUNT (sizeof(radio_modules_variables_text) / sizeof(char* const)) +const char* const radio_modules_variables_text[] = { + "Internal", + "External", +}; + +#define DEBUG_P_COUNT 2 +const char* const debug_pin_text[DEBUG_P_COUNT] = { + "OFF", + "A7", +}; + +static void subghz_scene_ext_module_changed(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + value_index = variable_item_get_current_value_index(item); + UNUSED(subghz); + + variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]); +} +static void subghz_ext_module_start_var_list_enter_callback(void* context, uint32_t index) { + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, index); +} + +static void subghz_scene_receiver_config_set_debug_pin(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, debug_pin_text[index]); + + subghz->txrx->debug_pin_state = index == 1; +} + +void subghz_scene_ext_module_settings_on_enter(void* context) { + SubGhz* subghz = context; + + VariableItemList* variable_item_list = subghz->variable_item_list; + + value_index = furi_hal_subghz.radio_type; + VariableItem* item = variable_item_list_add( + variable_item_list, "Module", EXT_MODULES_COUNT, subghz_scene_ext_module_changed, subghz); + + variable_item_list_set_enter_callback( + variable_item_list, subghz_ext_module_start_var_list_enter_callback, subghz); + + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + item = variable_item_list_add( + subghz->variable_item_list, + "Debug Pin:", + DEBUG_P_COUNT, + subghz_scene_receiver_config_set_debug_pin, + subghz); + value_index2 = subghz->txrx->debug_pin_state; + variable_item_set_current_value_index(item, value_index2); + variable_item_set_current_value_text(item, debug_pin_text[value_index2]); + } + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); +} + +bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + UNUSED(subghz); + UNUSED(event); + + // Set selected radio module + furi_hal_subghz_set_radio_type(value_index); + + // Check if module is present, if no -> show error + if(!furi_hal_subghz_check_radio()) { + value_index = 0; + furi_hal_subghz_set_radio_type(value_index); + furi_string_set(subghz->error_str, "Please connect\nexternal radio"); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); + } + + return false; +} + +void subghz_scene_ext_module_settings_on_exit(void* context) { + SubGhz* subghz = context; + variable_item_list_reset(subghz->variable_item_list); +} diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index fea4b6aef..e396527cc 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -119,9 +119,6 @@ void subghz_scene_read_raw_on_enter(void* context) { subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME); furi_assert(subghz->txrx->decoder_result); - // make sure we're not in auto-detect mode, which is only meant for the Read app - subghz_protocol_decoder_raw_set_auto_mode(subghz->txrx->decoder_result, false); - //set filter RAW feed subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_RAW); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); @@ -422,10 +419,6 @@ void subghz_scene_read_raw_on_exit(void* context) { subghz->state_notifications = SubGhzNotificationStateIDLE; notification_message(subghz->notifications, &sequence_reset_rgb); -//filter restoration -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - subghz_last_settings_set_detect_raw_values(subghz); -#else - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); -#endif + //filter restoration + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); } \ No newline at end of file diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index cab19f730..e1ea08497 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -1,6 +1,7 @@ #include "../subghz_i.h" #include "../views/receiver.h" #include +#include #define TAG "SubGhzSceneReceiver" @@ -53,7 +54,10 @@ static void subghz_scene_receiver_update_statusbar(void* context) { } else { subghz_get_frequency_modulation(subghz, frequency_str, NULL); furi_string_printf( - modulation_str, "Mod: %s", furi_string_get_cstr(subghz->txrx->preset->name)); + modulation_str, + "%s Mod: %s", + furi_hal_subghz_get_radio_type() ? "Ext" : "Int", + furi_string_get_cstr(subghz->txrx->preset->name)); } #else subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); @@ -157,6 +161,11 @@ void subghz_scene_receiver_on_enter(void* context) { } subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); + //to use a universal decoder, we are looking for a link to it + subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( + subghz->txrx->receiver, SUBGHZ_PROTOCOL_BIN_RAW_NAME); + furi_assert(subghz->txrx->decoder_result); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); } @@ -173,7 +182,6 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_sleep(subghz); } subghz->txrx->hopper_state = SubGhzHopperStateOFF; - subghz_history_set_hopper_state(subghz->txrx->history, false); subghz->txrx->idx_menu_chosen = 0; subghz_receiver_set_rx_callback(subghz->txrx->receiver, NULL, subghz); @@ -221,6 +229,13 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_hopper_update(subghz); subghz_scene_receiver_update_statusbar(subghz); } + + //get RSSI + float rssi = furi_hal_subghz_get_rssi(); + subghz_receiver_rssi(subghz->subghz_receiver, rssi); + subghz_protocol_decoder_bin_raw_data_input_rssi( + (SubGhzProtocolDecoderBinRAW*)subghz->txrx->decoder_result, rssi); + switch(subghz->state_notifications) { case SubGhzNotificationStateRx: notification_message(subghz->notifications, &sequence_blink_cyan_10); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index c23d93496..af4c6aca3 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -1,16 +1,11 @@ #include "../subghz_i.h" #include -#include - -#define TAG "SubGhzSceneReceiverConfig" - enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, - SubGhzSettingIndexDetectRaw, - SubGhzSettingIndexRSSIThreshold, + SubGhzSettingIndexBinRAW, SubGhzSettingIndexSound, SubGhzSettingIndexLock, SubGhzSettingIndexRAWThesholdRSSI, @@ -45,35 +40,6 @@ const float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = { -40.0f, }; -#define BANDWIDTH_COUNT 16 -const char* const bandwidth_labels[BANDWIDTH_COUNT] = { - "58 kHz", - "68 kHz", - "81 kHz", - "102 kHz", - "116 kHz", - "135 kHz", - "162 kHz", - "203 kHz", - "232 kHz", - "270 kHz", - "325 kHz", - "406 kHz", - "464 kHz", - "541 kHz", - "650 kHz", - "812 kHz", -}; - -// Bandwidths values are ordered from F (58kHz) to 0 (812kHz) -#define BANDWIDTH_INDEX(value) ((uint8_t)15 - ((uint8_t)(value >> 4) & 0x0F)) - -#define MANCHESTER_FLAG_COUNT 2 -const char* const manchester_flag_text[MANCHESTER_FLAG_COUNT] = { - "OFF", - "ON", -}; - #define HOPPING_COUNT 2 const char* const hopping_text[HOPPING_COUNT] = { "OFF", @@ -84,40 +50,6 @@ const uint32_t hopping_value[HOPPING_COUNT] = { SubGhzHopperStateRunnig, }; -#define DETECT_RAW_COUNT 2 -const char* const detect_raw_text[DETECT_RAW_COUNT] = { - "OFF", - "ON", -}; - -#ifndef SUBGHZ_SAVE_DETECT_RAW_SETTING -const SubGhzProtocolFlag detect_raw_value[DETECT_RAW_COUNT] = { - SubGhzProtocolFlag_Decodable, - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_RAW, -}; -#endif - -#define RSSI_THRESHOLD_COUNT 7 -const char* const rssi_threshold_text[RSSI_THRESHOLD_COUNT] = { - "-72db", - "-67db", - "-62db", - "-57db", - "-52db", - "-47db", - "-42db", -}; - -const int rssi_threshold_value[RSSI_THRESHOLD_COUNT] = { - -72, - -67, - -62, - -57, - -52, - -47, - -42, -}; - #define SPEAKER_COUNT 2 const char* const speaker_text[SPEAKER_COUNT] = { "OFF", @@ -127,18 +59,15 @@ const uint32_t speaker_value[SPEAKER_COUNT] = { SubGhzSpeakerStateShutdown, SubGhzSpeakerStateEnable, }; - -// Allow advanced edit only on specific preset -bool subghz_scene_receiver_config_can_edit_current_preset(SubGhz* subghz) { - SubGhzRadioPreset* preset = subghz->txrx->preset; - - bool preset_name_allow_edit = - !strcmp(furi_string_get_cstr(preset->name), ADVANCED_AM_PRESET_NAME) || - !strcmp(furi_string_get_cstr(preset->name), "CUSTOM"); - - return preset && preset_name_allow_edit && - subghz_preset_custom_is_ook_modulation(preset->data, preset->data_size); -} +#define BIN_RAW_COUNT 2 +const char* const bin_raw_text[BIN_RAW_COUNT] = { + "OFF", + "ON", +}; +const uint32_t bin_raw_value[BIN_RAW_COUNT] = { + SubGhzProtocolFlag_Decodable, + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, +}; uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { furi_assert(context); @@ -170,52 +99,6 @@ uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* return index; } -// Advanced settings of preset may change if preset was changed. -// In that case - update values -static void subghz_scene_receiver_config_update_advanced(SubGhz* subghz) { - uint8_t value_index; - - if(subghz->variable_item_bandwidth) { - value_index = BANDWIDTH_INDEX(subghz->txrx->raw_bandwidth); - variable_item_set_current_value_index(subghz->variable_item_bandwidth, value_index); - variable_item_set_current_value_text( - subghz->variable_item_bandwidth, bandwidth_labels[value_index]); - } - - if(subghz->variable_item_datarate) { - variable_item_set_current_value_index(subghz->variable_item_datarate, 0); - - char datarate_str[16] = {0}; - subghz_preset_custom_printf_datarate( - subghz->txrx->raw_datarate, datarate_str, sizeof(datarate_str)); - variable_item_set_current_value_text(subghz->variable_item_datarate, datarate_str); - } - - if(subghz->variable_item_manchester) { - value_index = subghz->txrx->raw_manchester_enabled ? 1 : 0; - - variable_item_set_current_value_index(subghz->variable_item_manchester, value_index); - variable_item_set_current_value_text( - subghz->variable_item_manchester, manchester_flag_text[value_index]); - } -} - -// Apply advanced configuration to advanced am preset -static void subghz_scene_receiver_config_apply_advanced(SubGhz* subghz) { - if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) { - SubGhzRadioPreset* preset = subghz->txrx->preset; - - subghz_preset_custom_set_bandwidth( - preset->data, preset->data_size, subghz->txrx->raw_bandwidth); - - subghz_preset_custom_set_machester_enable( - preset->data, preset->data_size, subghz->txrx->raw_manchester_enabled); - - subghz_preset_custom_set_datarate( - preset->data, preset->data_size, subghz->txrx->raw_datarate); - } -} - uint8_t subghz_scene_receiver_config_hopper_value_index( const uint32_t value, const uint32_t values[], @@ -236,36 +119,6 @@ uint8_t subghz_scene_receiver_config_hopper_value_index( } } -#ifndef SUBGHZ_SAVE_DETECT_RAW_SETTING -uint8_t subghz_scene_receiver_config_detect_raw_value_index( - const SubGhzProtocolFlag value, - const SubGhzProtocolFlag values[], - uint8_t values_count) { - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if(value == values[i]) { - index = i; - break; - } - } - return index; -} -#endif - -uint8_t subghz_scene_receiver_config_rssi_threshold_value_index( - const int value, - const int values[], - uint8_t values_count) { - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if(value == values[i]) { - index = i; - break; - } - } - return index; -} - static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -301,49 +154,12 @@ static void subghz_scene_receiver_config_set_preset(VariableItem* item) { subghz->txrx->preset->frequency, subghz_setting_get_preset_data(subghz->setting, index), subghz_setting_get_preset_data_size(subghz->setting, index)); - - subghz_scene_receiver_config_update_advanced(subghz); -} - -static void subghz_scene_receiver_config_set_rssi_threshold(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, rssi_threshold_text[index]); - subghz_protocol_decoder_raw_set_rssi_threshold( - subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME), - rssi_threshold_value[index]); -} - -static void subghz_scene_receiver_config_set_detect_raw(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - //if(subghz->txrx->hopper_state == 0) { - variable_item_set_current_value_text(item, detect_raw_text[index]); -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - subghz->last_settings->detect_raw = index; - - subghz_last_settings_set_detect_raw_values(subghz); -#else - subghz_receiver_set_filter(subghz->txrx->receiver, detect_raw_value[index]); - - subghz_protocol_decoder_raw_set_auto_mode( - subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME), - (index == 1)); -#endif - /*} else { - variable_item_set_current_value_index(item, 0); - }*/ } static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - //if(subghz_receiver_get_filter(subghz->txrx->receiver) == SubGhzProtocolFlag_Decodable) { variable_item_set_current_value_text(item, hopping_text[index]); if(hopping_value[index] == SubGhzHopperStateOFF) { char text_buf[10] = {0}; @@ -374,10 +190,6 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) } subghz->txrx->hopper_state = hopping_value[index]; - subghz_history_set_hopper_state(subghz->txrx->history, (index == 1)); - /*} else { - variable_item_set_current_value_index(item, 0); - }*/ } static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { @@ -388,6 +200,15 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { subghz->txrx->speaker_state = speaker_value[index]; } +static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, bin_raw_text[index]); + subghz->txrx->filter = bin_raw_value[index]; + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); +} + static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -396,107 +217,6 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it subghz->txrx->raw_threshold_rssi = raw_theshold_rssi_value[index]; } -static void subghz_scene_receiver_config_set_raw_ook_bandwidth(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) { - // update bandwidth value from selected index - uint8_t index = variable_item_get_current_value_index(item); - subghz->txrx->raw_bandwidth = subghz_preset_custom_bandwidth_values[index]; - - subghz_scene_receiver_config_update_advanced(subghz); - } else { - furi_string_set( - subghz->error_str, "Read-only\nsetting!\nUse '" ADVANCED_AM_PRESET_NAME "'\npreset."); - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneSettingError); - } -} - -static void subghz_scene_receiver_config_set_manchester_flag(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) { - // update enable flag from view - uint8_t index = variable_item_get_current_value_index(item); - subghz->txrx->raw_manchester_enabled = index == 0 ? false : true; - - subghz_scene_receiver_config_update_advanced(subghz); - } else { - furi_string_set( - subghz->error_str, "Read-only\nsetting!\nUse '" ADVANCED_AM_PRESET_NAME "'\npreset."); - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneSettingError); - } -} - -static void subghz_scene_receiver_config_datarate_input_callback(void* context) { - furi_assert(context); - SubGhz* subghz = context; - - float value = atoff(subghz->datarate_input_str); - if(value != 0 && value > 0) { - subghz->txrx->raw_datarate = value; - subghz_scene_receiver_config_update_advanced(subghz); - } - - // show list view - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); -} - -static bool subghz_scene_receiver_config_datarate_input_validate( - const char* text, - FuriString* error, - void* context) { - UNUSED(context); - - float value = atoff(text); - if(value == 0) { - furi_string_printf(error, "Cannot parse\r\nvalue"); - } else if(value < 0) { - furi_string_printf(error, "Value\r\nshould be\r\ngreater\r\nthan 0"); - } else { - return true; - } - - return false; -} - -static void subghz_scene_receiver_config_show_datarate_input(SubGhz* subghz) { - TextInput* text_input = subghz->text_input; - - snprintf( - subghz->datarate_input_str, - sizeof(subghz->datarate_input_str), - "%.2f", - (double)subghz->txrx->raw_datarate); - - text_input_set_header_text(text_input, "Datarate bauds (not kBauds)"); - text_input_set_result_callback( - text_input, - subghz_scene_receiver_config_datarate_input_callback, - subghz, - subghz->datarate_input_str, - sizeof(subghz->datarate_input_str), - false); - - text_input_set_validator( - text_input, subghz_scene_receiver_config_datarate_input_validate, NULL); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTextInput); -} - -static void subghz_scene_receiver_config_set_datarate(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) { - // reset value index in order to show '>' symbol always - variable_item_set_current_value_index(item, 0); - subghz_scene_receiver_config_show_datarate_input(subghz); - } else { - furi_string_set( - subghz->error_str, "Read-only\nsetting!\nUse '" ADVANCED_AM_PRESET_NAME "'\npreset."); - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneSettingError); - } -} - static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { furi_assert(context); SubGhz* subghz = context; @@ -511,13 +231,6 @@ void subghz_scene_receiver_config_on_enter(void* context) { VariableItem* item; uint8_t value_index; -#ifdef FURI_DEBUG - FURI_LOG_D( - TAG, - "Last frequency: %ld, Preset: %ld", - subghz->last_settings->frequency, - subghz->last_settings->preset); -#endif item = variable_item_list_add( subghz->variable_item_list, "Frequency:", @@ -563,52 +276,35 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, hopping_text[value_index]); + } - // Detect Raw + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { item = variable_item_list_add( subghz->variable_item_list, - "Detect Raw:", - DETECT_RAW_COUNT, - subghz_scene_receiver_config_set_detect_raw, + "Bin RAW:", + BIN_RAW_COUNT, + subghz_scene_receiver_config_set_bin_raw, subghz); -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - value_index = subghz->last_settings->detect_raw; -#else - value_index = subghz_scene_receiver_config_detect_raw_value_index( - subghz_receiver_get_filter(subghz->txrx->receiver), - detect_raw_value, - DETECT_RAW_COUNT); -#endif + value_index = value_index_uint32(subghz->txrx->filter, bin_raw_value, BIN_RAW_COUNT); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, detect_raw_text[value_index]); + variable_item_set_current_value_text(item, bin_raw_text[value_index]); + } - // RSSI - item = variable_item_list_add( - subghz->variable_item_list, - "RSSI for Raw:", - RSSI_THRESHOLD_COUNT, - subghz_scene_receiver_config_set_rssi_threshold, - subghz); - value_index = subghz_scene_receiver_config_rssi_threshold_value_index( - subghz_protocol_encoder_get_rssi_threshold(subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME)), - rssi_threshold_value, - RSSI_THRESHOLD_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, rssi_threshold_text[value_index]); + // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) + item = variable_item_list_add( + subghz->variable_item_list, + "Sound:", + SPEAKER_COUNT, + subghz_scene_receiver_config_set_speaker, + subghz); + value_index = value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, speaker_text[value_index]); + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { // Lock keyboard - item = variable_item_list_add( - subghz->variable_item_list, - "Sound:", - SPEAKER_COUNT, - subghz_scene_receiver_config_set_speaker, - subghz); - value_index = - value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, speaker_text[value_index]); - variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL); variable_item_list_set_enter_callback( subghz->variable_item_list, @@ -627,33 +323,6 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz->txrx->raw_threshold_rssi, raw_theshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]); - - // Advanced MODEM settings. RW only for ADVANCED_AM_PRESET_NAME - // Bandwidth - subghz->variable_item_bandwidth = variable_item_list_add( - subghz->variable_item_list, - "Bandwidth:", - BANDWIDTH_COUNT, - subghz_scene_receiver_config_set_raw_ook_bandwidth, - subghz); - - // Data rate (editable via OK click) - subghz->variable_item_datarate = variable_item_list_add( - subghz->variable_item_list, - "Data rate:", - 2, - subghz_scene_receiver_config_set_datarate, - subghz); - - // Manchester codec flag - subghz->variable_item_manchester = variable_item_list_add( - subghz->variable_item_list, - "Manch. Enc.:", - MANCHESTER_FLAG_COUNT, - subghz_scene_receiver_config_set_manchester_flag, - subghz); - - subghz_scene_receiver_config_update_advanced(subghz); } view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); } @@ -667,11 +336,6 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even subghz->lock = SubGhzLockOn; scene_manager_previous_scene(subghz->scene_manager); consumed = true; - } else if(event.event == SubGhzCustomEventSceneSettingError) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneShowErrorSub, event.event); - consumed = true; } } return consumed; @@ -679,16 +343,6 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even void subghz_scene_receiver_config_on_exit(void* context) { SubGhz* subghz = context; - - // reset UI variable list items (next scene may be not RAW config) - subghz->variable_item_bandwidth = NULL; - subghz->variable_item_datarate = NULL; - subghz->variable_item_manchester = NULL; - text_input_set_validator(subghz->text_input, NULL, NULL); - - // apply advanced preset variables (if applicable) - subghz_scene_receiver_config_apply_advanced(subghz); - variable_item_list_set_selected_item(subghz->variable_item_list, 0); variable_item_list_reset(subghz->variable_item_list); subghz_last_settings_save(subghz->last_settings); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index e9c849e1e..0b265cca2 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -92,8 +92,6 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) { // Removed static check if(((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) && - // disable "Send" for auto-captured RAW signals for now. They can still be saved and sent by loading them. - subghz->txrx->decoder_result->protocol->type != SubGhzProtocolTypeRAW && subghz->txrx->decoder_result->protocol->encoder->deserialize) { widget_add_button_element( subghz->widget, @@ -138,6 +136,21 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_history_get_raw_data( subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_tx_stop(subghz); + } + if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { + subghz_begin( + subghz, + subghz_setting_get_preset_data_by_name( + subghz->setting, + furi_string_get_cstr(subghz->txrx->preset->name))); + subghz_rx(subghz, subghz->txrx->preset->frequency); + } + if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { + subghz->txrx->hopper_state = SubGhzHopperStateRunnig; + } + subghz->state_notifications = SubGhzNotificationStateRx; } else { subghz->state_notifications = SubGhzNotificationStateTx; } diff --git a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c index 2943c764a..113e7ae74 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c @@ -26,16 +26,8 @@ bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventSceneShowErrorSub) { - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowErrorSub) == - SubGhzCustomEventSceneSettingError) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneReceiverConfig); - } else { - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneStart); - } + scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); return true; } } diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index 62e30784f..69e6cbea7 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -10,27 +10,9 @@ enum SubmenuIndex { SubmenuIndexAddManually, SubmenuIndexFrequencyAnalyzer, SubmenuIndexReadRAW, + SubmenuIndexExtSettings, }; -void subghz_scene_start_remove_advanced_preset(SubGhz* subghz) { - // delete operation is harmless - subghz_setting_delete_custom_preset(subghz->setting, ADVANCED_AM_PRESET_NAME); -} - -void subghz_scene_start_load_advanced_preset(SubGhz* subghz) { - for(uint8_t i = 0; i < subghz_setting_get_preset_count(subghz->setting); i++) { - if(!strcmp(subghz_setting_get_preset_name(subghz->setting, i), ADVANCED_AM_PRESET_NAME)) { - return; // already exists - } - } - - // Load custom advanced AM preset with configurable CFGMDM settings - FlipperFormat* advanced_am_preset = subghz_preset_custom_advanced_am_preset_alloc(); - subghz_setting_load_custom_preset( - subghz->setting, ADVANCED_AM_PRESET_NAME, advanced_am_preset); - flipper_format_free(advanced_am_preset); -} - void subghz_scene_start_submenu_callback(void* context, uint32_t index) { SubGhz* subghz = context; view_dispatcher_send_custom_event(subghz->view_dispatcher, index); @@ -41,15 +23,6 @@ void subghz_scene_start_on_enter(void* context) { if(subghz->state_notifications == SubGhzNotificationStateStarting) { subghz->state_notifications = SubGhzNotificationStateIDLE; } -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - subghz_last_settings_set_detect_raw_values(subghz); -#else - subghz_protocol_decoder_raw_set_auto_mode( - subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME), - false); - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); -#endif submenu_add_item( subghz->submenu, "Read", SubmenuIndexRead, subghz_scene_start_submenu_callback, subghz); @@ -73,6 +46,12 @@ void subghz_scene_start_on_enter(void* context) { SubmenuIndexFrequencyAnalyzer, subghz_scene_start_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "Radio Settings", + SubmenuIndexExtSettings, + subghz_scene_start_submenu_callback, + subghz); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { submenu_add_item( subghz->submenu, "Test", SubmenuIndexTest, subghz_scene_start_submenu_callback, subghz); @@ -91,15 +70,23 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { view_dispatcher_stop(subghz->view_dispatcher); return true; } else if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexReadRAW) { - subghz_scene_start_load_advanced_preset(subghz); + if(event.event == SubmenuIndexExtSettings) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexExtSettings); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneExtModuleSettings); + return true; + + } else if(!furi_hal_subghz_check_radio()) { + furi_string_set(subghz->error_str, "Please connect\nexternal radio"); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); + return true; + } else if(event.event == SubmenuIndexReadRAW) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW); subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); return true; } else if(event.event == SubmenuIndexRead) { - subghz_scene_start_remove_advanced_preset(subghz); scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); @@ -120,7 +107,6 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); return true; - } else if(event.event == SubmenuIndexTest) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest); diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index d95133da7..39e89e9e9 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -183,7 +183,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { //init setting subghz->setting = subghz_setting_alloc(); - subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user.txt")); + subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user")); // Custom Presets load without using config file @@ -208,37 +208,26 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { flipper_format_free(temp_fm_preset2); - // Pagers + // # HND - FM presets FlipperFormat* temp_fm_preset3 = flipper_format_string_alloc(); flipper_format_write_string_cstr( - temp_fm_preset2, + temp_fm_preset3, (const char*)"Custom_preset_data", - (const char*)"02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00"); + (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); flipper_format_rewind(temp_fm_preset3); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"Pagers", temp_fm_preset3); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_1", temp_fm_preset3); flipper_format_free(temp_fm_preset3); - // # HND - FM presets FlipperFormat* temp_fm_preset4 = flipper_format_string_alloc(); flipper_format_write_string_cstr( temp_fm_preset4, (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset4); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"Honda_1", temp_fm_preset4); - - flipper_format_free(temp_fm_preset3); - - FlipperFormat* temp_fm_preset5 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset5, - (const char*)"Custom_preset_data", (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset5); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"Honda_2", temp_fm_preset5); + flipper_format_rewind(temp_fm_preset4); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_2", temp_fm_preset4); - flipper_format_free(temp_fm_preset5); + flipper_format_free(temp_fm_preset4); // custom presets loading - end @@ -247,20 +236,11 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->last_settings = subghz_last_settings_alloc(); subghz_last_settings_load(subghz->last_settings, 0); #if FURI_DEBUG -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - FURI_LOG_D( - TAG, - "last frequency: %ld, preset: %ld, detect_raw: %d", - subghz->last_settings->frequency, - subghz->last_settings->preset, - subghz->last_settings->detect_raw); -#else FURI_LOG_D( TAG, "last frequency: %ld, preset: %ld", subghz->last_settings->frequency, subghz->last_settings->preset); -#endif #endif subghz_setting_set_default_frequency(subghz->setting, subghz->last_settings->frequency); } @@ -279,6 +259,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->txrx->hopper_state = SubGhzHopperStateOFF; subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; + subghz->txrx->debug_pin_state = false; if(!alloc_for_tx_only) { subghz->txrx->history = subghz_history_alloc(); } @@ -292,16 +273,15 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->txrx->environment = subghz_environment_alloc(); subghz_environment_set_came_atomo_rainbow_table_file_name( subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + subghz->txrx->environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry( subghz->txrx->environment, (void*)&subghz_protocol_registry); subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment); -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - subghz_last_settings_set_detect_raw_values(subghz); -#else - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); -#endif + subghz->txrx->filter = SubGhzProtocolFlag_Decodable; + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); subghz_worker_set_overrun_callback( subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); @@ -325,6 +305,8 @@ void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) { subghz->rpc_ctx = NULL; } + subghz_speaker_off(subghz); + #if FURI_DEBUG // Packet Test view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index ed1648083..c047a32b3 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -42,8 +42,9 @@ void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); frequency = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_cc1101_g0, true); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, true); furi_hal_power_suppress_charge_enter(); @@ -252,6 +253,8 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); subghz_environment_set_came_atomo_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); @@ -264,7 +267,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { furi_hal_subghz_reset(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); frequency = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_power_suppress_charge_enter(); @@ -304,6 +307,81 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { free(instance); } +void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + uint32_t frequency = 433920000; + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); + if(ret != 1) { + printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + cli_print_usage("subghz rx", "", furi_string_get_cstr(args)); + return; + } + if(!furi_hal_subghz_is_frequency_valid(frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", + frequency); + return; + } + } + + // Allocate context and buffers + SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); + instance->stream = + furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); + furi_check(instance->stream); + + // Configure radio + furi_hal_subghz_reset(); + furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok270Async); + frequency = furi_hal_subghz_set_frequency_and_path(frequency); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + + furi_hal_power_suppress_charge_enter(); + + // Prepare and start RX + furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance); + + // Wait for packets to arrive + printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency); + LevelDuration level_duration; + size_t counter = 0; + while(!cli_cmd_interrupt_received(cli)) { + int ret = furi_stream_buffer_receive( + instance->stream, &level_duration, sizeof(LevelDuration), 10); + if(ret == 0) { + continue; + } + if(ret != sizeof(LevelDuration)) { + puts("stream corrupt"); + break; + } + if(level_duration_is_reset(level_duration)) { + puts(". "); + } else { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + printf("%c%lu ", level ? '+' : '-', duration); + } + furi_thread_stdout_flush(); + counter++; + if(counter > 255) { + puts("\r\n"); + counter = 0; + } + } + + // Shutdown radio + furi_hal_subghz_stop_async_rx(); + furi_hal_subghz_sleep(); + + furi_hal_power_suppress_charge_exit(); + + // Cleanup + furi_stream_buffer_free(instance->stream); + free(instance); +} void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { UNUSED(context); FuriString* file_name; @@ -372,6 +450,8 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { } subghz_environment_set_came_atomo_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); @@ -387,7 +467,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { } printf( - "Listening at %s.\r\n\r\nPress CTRL+C to stop\r\n\r\n", + "Listening at \033[0;33m%s\033[0m.\r\n\r\nPress CTRL+C to stop\r\n\r\n", furi_string_get_cstr(file_name)); LevelDuration level_duration; @@ -426,7 +506,8 @@ static void subghz_cli_command_print_usage() { printf("\tchat \t - Chat with other Flippers\r\n"); printf( "\ttx <3 byte Key: in hex> \t - Transmitting key\r\n"); - printf("\trx \t - Reception key\r\n"); + printf("\trx \t - Receive\r\n"); + printf("\trx_raw \t - Receive RAW\r\n"); printf("\tdecode_raw \t - Testing\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { @@ -521,8 +602,7 @@ static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) { furi_string_free(source); } -static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) { - UNUSED(context); +static void subghz_cli_command_chat(Cli* cli, FuriString* args) { uint32_t frequency = 433920000; if(furi_string_size(args)) { @@ -578,7 +658,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - furi_string_printf(name, "%s: ", furi_hal_version_get_name_ptr()); + furi_string_printf(name, "\033[0;33m%s\033[0m: ", furi_hal_version_get_name_ptr()); furi_string_set(input, name); printf("%s", furi_string_get_cstr(input)); fflush(stdout); @@ -659,14 +739,18 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) { notification_message(notification, &sequence_single_vibro); break; case SubGhzChatEventUserEntrance: - furi_string_printf(sysmsg, "%s joined chat.\r\n", furi_hal_version_get_name_ptr()); + furi_string_printf( + sysmsg, + "\033[0;34m%s joined chat.\033[0m\r\n", + furi_hal_version_get_name_ptr()); subghz_chat_worker_write( subghz_chat, (uint8_t*)furi_string_get_cstr(sysmsg), strlen(furi_string_get_cstr(sysmsg))); break; case SubGhzChatEventUserExit: - furi_string_printf(sysmsg, "%s left chat.\r\n", furi_hal_version_get_name_ptr()); + furi_string_printf( + sysmsg, "\033[0;31m%s left chat.\033[0m\r\n", furi_hal_version_get_name_ptr()); subghz_chat_worker_write( subghz_chat, (uint8_t*)furi_string_get_cstr(sysmsg), @@ -711,7 +795,7 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "chat") == 0) { - subghz_cli_command_chat(cli, args, NULL); + subghz_cli_command_chat(cli, args); break; } @@ -725,6 +809,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { break; } + if(furi_string_cmp_str(cmd, "rx_raw") == 0) { + subghz_cli_command_rx_raw(cli, args, context); + break; + } + if(furi_string_cmp_str(cmd, "decode_raw") == 0) { subghz_cli_command_decode_raw(cli, args, context); break; @@ -763,11 +852,9 @@ void subghz_on_system_start() { Cli* cli = furi_record_open(RECORD_CLI); cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL); - // psst RM... i know you dont care much about errors, but if you ever see this... incompatible pointer type :3 - cli_add_command(cli, "chat", CliCommandFlagDefault, subghz_cli_command_chat, NULL); furi_record_close(RECORD_CLI); #else UNUSED(subghz_cli_command); #endif -} \ No newline at end of file +} diff --git a/applications/main/subghz/subghz_file_encoder_worker.c b/applications/main/subghz/subghz_file_encoder_worker.c new file mode 100644 index 000000000..a8c6519ef --- /dev/null +++ b/applications/main/subghz/subghz_file_encoder_worker.c @@ -0,0 +1,244 @@ +#include "subghz_file_encoder_worker.h" + +#include +#include +#include + +#define TAG "SubGhzFileEncoderWorker" + +#define SUBGHZ_FILE_ENCODER_LOAD 512 + +struct SubGhzFileEncoderWorker { + FuriThread* thread; + FuriStreamBuffer* stream; + + Storage* storage; + FlipperFormat* flipper_format; + + volatile bool worker_running; + volatile bool worker_stoping; + bool level; + bool is_storage_slow; + FuriString* str_data; + FuriString* file_path; + + SubGhzFileEncoderWorkerCallbackEnd callback_end; + void* context_end; +}; + +void subghz_file_encoder_worker_callback_end( + SubGhzFileEncoderWorker* instance, + SubGhzFileEncoderWorkerCallbackEnd callback_end, + void* context_end) { + furi_assert(instance); + furi_assert(callback_end); + instance->callback_end = callback_end; + instance->context_end = context_end; +} + +void subghz_file_encoder_worker_add_level_duration( + SubGhzFileEncoderWorker* instance, + int32_t duration) { + bool res = true; + if(duration < 0 && !instance->level) { + res = false; + } else if(duration > 0 && instance->level) { + res = false; + } + + if(res) { + instance->level = !instance->level; + furi_stream_buffer_send(instance->stream, &duration, sizeof(int32_t), 100); + } else { + FURI_LOG_E(TAG, "Invalid level in the stream"); + } +} + +bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) { + char* str1; + bool res = false; + // Line sample: "RAW_Data: -1, 2, -2..." + + // Look for a key in the line + str1 = strstr(strStart, "RAW_Data: "); + + if(str1 != NULL) { + // Skip key + str1 = strchr(str1, ' '); + + // Check that there is still an element in the line + while(strchr(str1, ' ') != NULL) { + str1 = strchr(str1, ' '); + + // Skip space + str1 += 1; + subghz_file_encoder_worker_add_level_duration(instance, atoi(str1)); + } + res = true; + } + return res; +} + +void subghz_file_encoder_worker_get_text_progress( + SubGhzFileEncoderWorker* instance, + FuriString* output) { + UNUSED(output); + Stream* stream = flipper_format_get_raw_stream(instance->flipper_format); + size_t total_size = stream_size(stream); + size_t current_offset = stream_tell(stream); + size_t buffer_avail = furi_stream_buffer_bytes_available(instance->stream); + + furi_string_printf(output, "%03u%%", 100 * (current_offset - buffer_avail) / total_size); +} + +LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) { + furi_assert(context); + SubGhzFileEncoderWorker* instance = context; + int32_t duration; + int ret = furi_stream_buffer_receive(instance->stream, &duration, sizeof(int32_t), 0); + if(ret == sizeof(int32_t)) { + LevelDuration level_duration = {.level = LEVEL_DURATION_RESET}; + if(duration < 0) { + level_duration = level_duration_make(false, -duration); + } else if(duration > 0) { + level_duration = level_duration_make(true, duration); + } else if(duration == 0) { //-V547 + level_duration = level_duration_reset(); + FURI_LOG_I(TAG, "Stop transmission"); + instance->worker_stoping = true; + } + return level_duration; + } else { + instance->is_storage_slow = true; + return level_duration_wait(); + } +} + +/** Worker thread + * + * @param context + * @return exit code + */ +static int32_t subghz_file_encoder_worker_thread(void* context) { + SubGhzFileEncoderWorker* instance = context; + FURI_LOG_I(TAG, "Worker start"); + bool res = false; + instance->is_storage_slow = false; + Stream* stream = flipper_format_get_raw_stream(instance->flipper_format); + do { + if(!flipper_format_file_open_existing( + instance->flipper_format, furi_string_get_cstr(instance->file_path))) { + FURI_LOG_E( + TAG, + "Unable to open file for read: %s", + furi_string_get_cstr(instance->file_path)); + break; + } + if(!flipper_format_read_string(instance->flipper_format, "Protocol", instance->str_data)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + + //skip the end of the previous line "\n" + stream_seek(stream, 1, StreamOffsetFromCurrent); + res = true; + instance->worker_stoping = false; + FURI_LOG_I(TAG, "Start transmission"); + } while(0); + + while(res && instance->worker_running) { + size_t stream_free_byte = furi_stream_buffer_spaces_available(instance->stream); + if((stream_free_byte / sizeof(int32_t)) >= SUBGHZ_FILE_ENCODER_LOAD) { + if(stream_read_line(stream, instance->str_data)) { + furi_string_trim(instance->str_data); + if(!subghz_file_encoder_worker_data_parse( + instance, furi_string_get_cstr(instance->str_data))) { + subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); + break; + } + } else { + subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); + break; + } + } else { + furi_delay_ms(1); + } + } + //waiting for the end of the transfer + if(instance->is_storage_slow) { + FURI_LOG_E(TAG, "Storage is slow"); + } + FURI_LOG_I(TAG, "End read file"); + while(!furi_hal_subghz_is_async_tx_complete() && instance->worker_running) { + furi_delay_ms(5); + } + FURI_LOG_I(TAG, "End transmission"); + while(instance->worker_running) { + if(instance->worker_stoping) { + if(instance->callback_end) instance->callback_end(instance->context_end); + } + furi_delay_ms(50); + } + flipper_format_file_close(instance->flipper_format); + + FURI_LOG_I(TAG, "Worker stop"); + return 0; +} + +SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { + SubGhzFileEncoderWorker* instance = malloc(sizeof(SubGhzFileEncoderWorker)); + + instance->thread = + furi_thread_alloc_ex("SubGhzFEWorker", 2048, subghz_file_encoder_worker_thread, instance); + instance->stream = furi_stream_buffer_alloc(sizeof(int32_t) * 2048, sizeof(int32_t)); + + instance->storage = furi_record_open(RECORD_STORAGE); + instance->flipper_format = flipper_format_file_alloc(instance->storage); + + instance->str_data = furi_string_alloc(); + instance->file_path = furi_string_alloc(); + instance->level = false; + instance->worker_stoping = true; + + return instance; +} + +void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance) { + furi_assert(instance); + + furi_stream_buffer_free(instance->stream); + furi_thread_free(instance->thread); + + furi_string_free(instance->str_data); + furi_string_free(instance->file_path); + + flipper_format_free(instance->flipper_format); + furi_record_close(RECORD_STORAGE); + + free(instance); +} + +bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(!instance->worker_running); + + furi_stream_buffer_reset(instance->stream); + furi_string_set(instance->file_path, file_path); + instance->worker_running = true; + furi_thread_start(instance->thread); + + return true; +} + +void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance) { + furi_assert(instance); + furi_assert(instance->worker_running); + + instance->worker_running = false; + furi_thread_join(instance->thread); +} + +bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance) { + furi_assert(instance); + return instance->worker_running; +} diff --git a/applications/main/subghz/subghz_file_encoder_worker.h b/applications/main/subghz/subghz_file_encoder_worker.h new file mode 100644 index 000000000..19a46f1e6 --- /dev/null +++ b/applications/main/subghz/subghz_file_encoder_worker.h @@ -0,0 +1,65 @@ +#pragma once + +#include + +typedef void (*SubGhzFileEncoderWorkerCallbackEnd)(void* context); + +typedef struct SubGhzFileEncoderWorker SubGhzFileEncoderWorker; + +/** + * End callback SubGhzWorker. + * @param instance SubGhzFileEncoderWorker instance + * @param callback SubGhzFileEncoderWorkerCallbackEnd callback + */ +void subghz_file_encoder_worker_callback_end( + SubGhzFileEncoderWorker* instance, + SubGhzFileEncoderWorkerCallbackEnd callback_end, + void* context_end); + +/** + * Allocate SubGhzFileEncoderWorker. + * @return SubGhzFileEncoderWorker* pointer to a SubGhzFileEncoderWorker instance + */ +SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc(); + +/** + * Free SubGhzFileEncoderWorker. + * @param instance Pointer to a SubGhzFileEncoderWorker instance + */ +void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance); + +/** + * Get a description of the progress. + * @param instance Pointer to a SubGhzFileEncoderWorker instance + * @param output + */ +void subghz_file_encoder_worker_get_text_progress( + SubGhzFileEncoderWorker* instance, + FuriString* output); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzFileEncoderWorker instance + * @return LevelDuration + */ +LevelDuration subghz_file_encoder_worker_get_level_duration(void* context); + +/** + * Start SubGhzFileEncoderWorker. + * @param instance Pointer to a SubGhzFileEncoderWorker instance + * @return bool - true if ok + */ +bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path); + +/** + * Stop SubGhzFileEncoderWorker + * @param instance Pointer to a SubGhzFileEncoderWorker instance + */ +void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance); + +/** + * Check if worker is running + * @param instance Pointer to a SubGhzFileEncoderWorker instance + * @return bool - true if running + */ +bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index e8d3acfd7..184146698 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -1,33 +1,15 @@ #include "subghz_history.h" -#include "subghz_history_private.h" #include -#include -#include -#include "flipper_format_stream_i.h" -#include -#define SUBGHZ_HISTORY_MAX 60 - -/** - * @brief Settings for temporary files - * - */ -#define SUBGHZ_HISTORY_TMP_DIR EXT_PATH("subghz/tmp_history") -#define SUBGHZ_HISTORY_TMP_EXTENSION ".tmp" -#define SUBGHZ_HISTORY_TMP_SIGNAL_MAX 700 -#define SUBGHZ_HISTORY_TMP_SIGNAL_MIN 100 -#define SUBGHZ_HISTORY_TMP_REMOVE_FILES true -#define SUBGHZ_HISTORY_TMP_RAW_KEY "RAW_Data" -#define MAX_LINE 500 -const size_t buffer_size = 32; +#include +#define SUBGHZ_HISTORY_MAX 55 +#define SUBGHZ_HISTORY_FREE_HEAP 20480 #define TAG "SubGhzHistory" typedef struct { FuriString* item_str; FlipperFormat* flipper_string; - FuriString* protocol_name; - bool is_file; uint8_t type; SubGhzRadioPreset* preset; } SubGhzHistoryItem; @@ -45,146 +27,30 @@ struct SubGhzHistory { uint16_t last_index_write; uint8_t code_last_hash_data; FuriString* tmp_string; - bool write_tmp_files; - bool is_hopper_running; - Storage* storage; SubGhzHistoryStruct* history; }; -#ifdef FURI_DEBUG -#define LOG_DELAY 0 -#endif - -FuriString* subghz_history_generate_temp_filename(uint32_t index) { - FuriHalRtcDateTime datetime = {0}; - furi_hal_rtc_get_datetime(&datetime); - return furi_string_alloc_printf("%03ld%s", index, SUBGHZ_HISTORY_TMP_EXTENSION); -} - -bool subghz_history_is_tmp_dir_exists(SubGhzHistory* instance) { - FileInfo file_info; - FS_Error error = storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &file_info); - - if(error == FSE_OK) { - if(file_info.flags & FSF_DIRECTORY) { - return true; - } - } - - return false; -} - -bool subghz_history_check_sdcard(SubGhzHistory* instance) { -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "check_sdcard"); - uint32_t start_time = furi_get_tick(); -#endif - - bool result = false; - // Stage 0 - check SD Card - FS_Error status = storage_sd_status(instance->storage); - if(status == FSE_OK) { - result = subghz_history_is_tmp_dir_exists(instance); - if(!subghz_history_is_tmp_dir_exists(instance)) { - result = storage_simply_mkdir(instance->storage, SUBGHZ_HISTORY_TMP_DIR); - } - } else { - FURI_LOG_W(TAG, "SD storage not installed! Status: %d", status); - } -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Running time (check_sdcard): %ld ms", furi_get_tick() - start_time); -#endif - - return result; -} - -void subghz_history_clear_tmp_dir(SubGhzHistory* instance) { - furi_assert(instance); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "clear_tmp_dir"); -#endif - - if(!instance->write_tmp_files) { - // Nothing to do here! - return; - } - //uint32_t start_time = furi_get_tick(); -#ifdef SUBGHZ_HISTORY_TMP_REMOVE_FILES - // Stage 0 - Dir exists? - bool res = subghz_history_is_tmp_dir_exists(instance); - if(res) { - // Stage 1 - delete all content if exists - FileInfo fileinfo; - storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &fileinfo); - - res = fileinfo.flags & FSF_DIRECTORY ? - storage_simply_remove_recursive(instance->storage, SUBGHZ_HISTORY_TMP_DIR) : - (storage_common_remove(instance->storage, SUBGHZ_HISTORY_TMP_DIR) == FSE_OK); - } - - // Stage 2 - create dir if necessary - res = storage_simply_mkdir(instance->storage, SUBGHZ_HISTORY_TMP_DIR); - if(!res) { - FURI_LOG_E(TAG, "Cannot process temp dir!"); - } -#endif - /* uint32_t stop_time = furi_get_tick() - start_time; - FURI_LOG_I(TAG, "Running time (clear_tmp_dir): %d ms", stop_time);*/ -} - SubGhzHistory* subghz_history_alloc(void) { SubGhzHistory* instance = malloc(sizeof(SubGhzHistory)); instance->tmp_string = furi_string_alloc(); instance->history = malloc(sizeof(SubGhzHistoryStruct)); SubGhzHistoryItemArray_init(instance->history->data); - instance->storage = furi_record_open(RECORD_STORAGE); - instance->write_tmp_files = subghz_history_check_sdcard(instance); - - instance->is_hopper_running = false; - - if(!instance->write_tmp_files) { - FURI_LOG_E(TAG, "Unstable work! Cannot use SD Card!"); - } - return instance; } -void subghz_history_item_free(void* current_item) { - furi_assert(current_item); - SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item; - furi_string_free(item->item_str); - furi_string_free(item->preset->name); - furi_string_free(item->protocol_name); - - free(item->preset); - item->type = 0; - item->is_file = false; - - if(item->flipper_string != NULL) { - flipper_format_free(item->flipper_string); - } -} - -void subghz_history_clean_item_array(SubGhzHistory* instance) { - for - M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { - subghz_history_item_free(item); - } -} - void subghz_history_free(SubGhzHistory* instance) { furi_assert(instance); furi_string_free(instance->tmp_string); - - subghz_history_clean_item_array(instance); + for + M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { + furi_string_free(item->item_str); + furi_string_free(item->preset->name); + free(item->preset); + flipper_format_free(item->flipper_string); + item->type = 0; + } SubGhzHistoryItemArray_clear(instance->history->data); free(instance->history); - - // Delete all temporary file, on exit it's ok - subghz_history_clear_tmp_dir(instance); - - furi_record_close(RECORD_STORAGE); - free(instance); } @@ -209,20 +75,19 @@ const char* subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx) { void subghz_history_reset(SubGhzHistory* instance) { furi_assert(instance); furi_string_reset(instance->tmp_string); - - subghz_history_clean_item_array(instance); - + for + M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { + furi_string_free(item->item_str); + furi_string_free(item->preset->name); + free(item->preset); + flipper_format_free(item->flipper_string); + item->type = 0; + } SubGhzHistoryItemArray_reset(instance->history->data); instance->last_index_write = 0; instance->code_last_hash_data = 0; } -void subghz_history_set_hopper_state(SubGhzHistory* instance, bool hopper_state) { - furi_assert(instance); - - instance->is_hopper_running = hopper_state; -} - uint16_t subghz_history_get_item(SubGhzHistory* instance) { furi_assert(instance); return instance->last_index_write; @@ -237,8 +102,12 @@ uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx) const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); - - return furi_string_get_cstr(item->protocol_name); + flipper_format_rewind(item->flipper_string); + if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { + FURI_LOG_E(TAG, "Missing Protocol"); + furi_string_reset(instance->tmp_string); + } + return furi_string_get_cstr(instance->tmp_string); } FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx) { @@ -247,72 +116,27 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx if(item->flipper_string) { return item->flipper_string; } else { - bool result_ok = false; - if(instance->write_tmp_files && item->is_file) { - // We have files! - FuriString* filename = subghz_history_generate_temp_filename(idx); - FuriString* dir_path; - - dir_path = furi_string_alloc_printf( - "%s/%s", SUBGHZ_HISTORY_TMP_DIR, furi_string_get_cstr(filename)); - - if(storage_file_exists(instance->storage, furi_string_get_cstr(dir_path))) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Exist: %s", furi_string_get_cstr(dir_path)); - furi_delay_ms(LOG_DELAY); -#endif - // Set to current anyway it has NULL value - item->flipper_string = flipper_format_string_alloc(); - Stream* dst_stream = flipper_format_get_raw_stream(item->flipper_string); - stream_clean(dst_stream); - - size_t size = stream_load_from_file( - dst_stream, instance->storage, furi_string_get_cstr(dir_path)); - if(size > 0) { -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Save ok!"); - furi_delay_ms(LOG_DELAY); -#endif - // We changed contents of file, so we no needed to load - // content from disk for the next time - item->is_file = false; - result_ok = true; - } else { - FURI_LOG_E(TAG, "Stream copy failed!"); - flipper_format_free(item->flipper_string); - } - } else { - FURI_LOG_E(TAG, "Can't convert filename to file"); - } - - furi_string_free(filename); - furi_string_free(dir_path); - } else { -#ifdef FURI_DEBUG - FURI_LOG_W(TAG, "Write TMP files failed!"); - furi_delay_ms(LOG_DELAY); -#endif - } - return result_ok ? item->flipper_string : NULL; + return NULL; } } - bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) { furi_assert(instance); - if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { - if(output != NULL) furi_string_printf(output, "Memory is FULL"); + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { + if(output != NULL) furi_string_printf(output, " Free heap LOW"); return true; } - if(output != NULL) { - furi_string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); + if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { + if(output != NULL) furi_string_printf(output, " Memory is FULL"); + return true; } + if(output != NULL) + furi_string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); return false; } uint16_t subghz_history_get_last_index(SubGhzHistory* instance) { return instance->last_index_write; } - void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx) { SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); furi_string_set(output, item->item_str); @@ -325,9 +149,8 @@ bool subghz_history_add_to_history( furi_assert(instance); furi_assert(context); - if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) { - return false; - } + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return false; + if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false; SubGhzProtocolDecoderBase* decoder_base = context; if((instance->code_last_hash_data == @@ -339,6 +162,7 @@ bool subghz_history_add_to_history( instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); instance->last_update_timestamp = furi_get_tick(); + FuriString* text; text = furi_string_alloc(); SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); @@ -351,11 +175,6 @@ bool subghz_history_add_to_history( item->preset->data_size = preset->data_size; item->item_str = furi_string_alloc(); - item->protocol_name = furi_string_alloc(); - - bool tmp_file_for_raw = false; - - // At this point file mapped to memory otherwise file cannot decode item->flipper_string = flipper_format_string_alloc(); subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset); @@ -367,30 +186,8 @@ bool subghz_history_add_to_history( if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { FURI_LOG_E(TAG, "Missing Protocol"); break; - } else { - furi_string_printf( - item->protocol_name, "%s", furi_string_get_cstr(instance->tmp_string)); } - if(!strcmp(furi_string_get_cstr(instance->tmp_string), "RAW")) { - // Check if hopper enabled we need to add little delay - if(instance->is_hopper_running) { - furi_delay_ms(40); - } - // Enable writing temp files to micro sd - tmp_file_for_raw = true; - // Write display name - furi_string_printf( - item->item_str, - "RAW %03ld.%02ld", - preset->frequency / 1000000 % 1000, - preset->frequency / 10000 % 100); - // Rewind - if(!flipper_format_rewind(item->flipper_string)) { - FURI_LOG_E(TAG, "Rewind error"); - } - - break; - } else if(!strcmp(furi_string_get_cstr(instance->tmp_string), "KeeLoq")) { + if(!strcmp(furi_string_get_cstr(instance->tmp_string), "KeeLoq")) { furi_string_set(instance->tmp_string, "KL "); if(!flipper_format_read_string(item->flipper_string, "Manufacture", text)) { FURI_LOG_E(TAG, "Missing Protocol"); @@ -411,484 +208,34 @@ bool subghz_history_add_to_history( } uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex(item->flipper_string, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Key"); - break; + FURI_LOG_D(TAG, "No Key"); } uint64_t data = 0; for(uint8_t i = 0; i < sizeof(uint64_t); i++) { data = (data << 8) | key_data[i]; } - if(!(uint32_t)(data >> 32)) { - furi_string_printf( - item->item_str, - "%s %lX", - furi_string_get_cstr(instance->tmp_string), - (uint32_t)(data & 0xFFFFFFFF)); + if(data != 0) { + if(!(uint32_t)(data >> 32)) { + furi_string_printf( + item->item_str, + "%s %lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data & 0xFFFFFFFF)); + } else { + furi_string_printf( + item->item_str, + "%s %lX%08lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data >> 32), + (uint32_t)(data & 0xFFFFFFFF)); + } } else { - furi_string_printf( - item->item_str, - "%s %lX%08lX", - furi_string_get_cstr(instance->tmp_string), - (uint32_t)(data >> 32), - (uint32_t)(data & 0xFFFFFFFF)); + furi_string_printf(item->item_str, "%s", furi_string_get_cstr(instance->tmp_string)); } + } while(false); - // If we can write to files - if(instance->write_tmp_files && tmp_file_for_raw) { - FuriString* filename = subghz_history_generate_temp_filename(instance->last_index_write); - FuriString* dir_path; - dir_path = furi_string_alloc(); - - furi_string_cat_printf( - dir_path, "%s/%s", SUBGHZ_HISTORY_TMP_DIR, furi_string_get_cstr(filename)); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Save temp file: %s", furi_string_get_cstr(dir_path)); -#endif - if(!subghz_history_tmp_write_file_split(instance, item, furi_string_get_cstr(dir_path))) { - // Plan B! - subghz_history_tmp_write_file_full(instance, item, dir_path); - } - if(item->is_file) { - flipper_format_free(item->flipper_string); - item->flipper_string = NULL; - } - furi_string_free(filename); - furi_string_free(dir_path); - - } else { -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Old fashion way"); -#endif - } - furi_string_free(text); - instance->last_index_write++; return true; } - -static inline bool is_space_playground(char c) { - return c == ' ' || c == '\t' || c == flipper_format_eolr; -} - -bool subghz_history_stream_read_valid_key(Stream* stream, FuriString* key) { - furi_string_reset(key); - uint8_t buffer[buffer_size]; - - bool found = false; - bool error = false; - bool accumulate = true; - bool new_line = true; - - while(true) { - size_t was_read = stream_read(stream, buffer, buffer_size); - if(was_read == 0) break; - - for(size_t i = 0; i < was_read; i++) { - uint8_t data = buffer[i]; - if(data == flipper_format_eoln) { - // EOL found, clean data, start accumulating data and set the new_line flag - furi_string_reset(key); - accumulate = true; - new_line = true; - } else if(data == flipper_format_eolr) { - // ignore - } else if(data == flipper_format_comment && new_line) { - // if there is a comment character and we are at the beginning of a new line - // do not accumulate comment data and reset the new_line flag - accumulate = false; - new_line = false; - } else if(data == flipper_format_delimiter) { - if(new_line) { - // we are on a "new line" and found the delimiter - // this can only be if we have previously found some kind of key, so - // clear the data, set the flag that we no longer want to accumulate data - // and reset the new_line flag - furi_string_reset(key); - accumulate = false; - new_line = false; - } else { - // parse the delimiter only if we are accumulating data - if(accumulate) { - // we found the delimiter, move the rw pointer to the delimiter location - // and signal that we have found something - if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { - error = true; - break; - } - - found = true; - break; - } - } - } else { - // just new symbol, reset the new_line flag - new_line = false; - if(accumulate) { - // and accumulate data if we want - furi_string_push_back(key, data); - } - } - } - - if(found || error) break; - } - - return found; -} - -bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode) { - bool found = false; - FuriString* read_key; - - read_key = furi_string_alloc(); - - while(!stream_eof(stream)) { - if(subghz_history_stream_read_valid_key(stream, read_key)) { - if(furi_string_cmp_str(read_key, key) == 0) { - if(!stream_seek(stream, 2, StreamOffsetFromCurrent)) { - break; - } - found = true; - break; - } else if(strict_mode) { - found = false; - break; - } - } - } - furi_string_free(read_key); - - return found; -} - -bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last) { - enum { LeadingSpace, ReadValue, TrailingSpace } state = LeadingSpace; - const size_t buffer_size = 32; - uint8_t buffer[buffer_size]; - bool result = false; - bool error = false; - - furi_string_reset(value); - - while(true) { - size_t was_read = stream_read(stream, buffer, buffer_size); - - if(was_read == 0) { - if(state != LeadingSpace && stream_eof(stream)) { - result = true; - *last = true; - } else { - error = true; - } - } - - for(uint16_t i = 0; i < was_read; i++) { - const uint8_t data = buffer[i]; - - if(state == LeadingSpace) { - if(is_space_playground(data)) { - continue; - } else if(data == flipper_format_eoln) { - stream_seek(stream, i - was_read, StreamOffsetFromCurrent); - error = true; - break; - } else { - state = ReadValue; - furi_string_push_back(value, data); - } - } else if(state == ReadValue) { - if(is_space_playground(data)) { - state = TrailingSpace; - } else if(data == flipper_format_eoln) { - if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { - error = true; - } else { - result = true; - *last = true; - } - break; - } else { - furi_string_push_back(value, data); - } - } else if(state == TrailingSpace) { - if(is_space_playground(data)) { - continue; - } else if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { - error = true; - } else { - *last = (data == flipper_format_eoln); - result = true; - } - break; - } - } - - if(error || result) break; - } - - return result; -} - -bool subghz_history_read_int32(Stream* stream, int32_t* _data, const uint16_t data_size) { - bool result = false; - result = true; - FuriString* value; - value = furi_string_alloc(); - - for(size_t i = 0; i < data_size; i++) { - bool last = false; - result = subghz_history_stream_read_value(stream, value, &last); - if(result) { - int scan_values = 0; - - int32_t* data = _data; - scan_values = sscanf(furi_string_get_cstr(value), "%" PRIi32, &data[i]); - - if(scan_values != 1) { - result = false; - break; - } - } else { - break; - } - - if(last && ((i + 1) != data_size)) { - result = false; - break; - } - } - - furi_string_free(value); - return result; -} - -uint32_t subghz_history_rand_range(uint32_t min, uint32_t max) { - // size of range, inclusive - const uint32_t length_of_range = max - min + 1; - - // add n so that we don't return a number below our range - return (uint32_t)(rand() % length_of_range + min); -} - -bool subghz_history_write_file_noise( - Stream* file, - bool is_negative_start, - size_t current_position, - bool empty_line) { - size_t was_write = 0; - if(empty_line) { - was_write = stream_write_format(file, "%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY); - - if(was_write <= 0) { - FURI_LOG_E(TAG, "Can't write key!"); - return false; - } - } - - int8_t first; - int8_t second; - if(is_negative_start) { - first = -1; - second = 1; - } else { - first = 1; - second = -1; - } - while(current_position < MAX_LINE) { - was_write = stream_write_format( - file, - "%ld %ld ", - subghz_history_rand_range( - SUBGHZ_HISTORY_TMP_SIGNAL_MIN, SUBGHZ_HISTORY_TMP_SIGNAL_MAX) * - first, - subghz_history_rand_range( - SUBGHZ_HISTORY_TMP_SIGNAL_MIN, SUBGHZ_HISTORY_TMP_SIGNAL_MAX) * - second); - - if(was_write <= 0) { - FURI_LOG_E(TAG, "Can't write random values!"); - return false; - } - - current_position += was_write; - } - - // Step back to write \n instead of space - size_t offset = stream_tell(file); - if(stream_seek(file, offset - 1, StreamOffsetFromCurrent)) { - FURI_LOG_E(TAG, "Step back failed!"); - return false; - } - - return stream_write_char(file, flipper_format_eoln) > 0; -} - -bool subghz_history_write_file_data( - Stream* src, - Stream* file, - bool* is_negative_start, - size_t* current_position) { - size_t offset_file = 0; - bool result = false; - int32_t value = 0; - - do { - if(!subghz_history_read_int32(src, &value, 1)) { - result = true; - break; - } - offset_file = stream_tell(file); - stream_write_format(file, "%ld ", value); - *current_position += stream_tell(file) - offset_file; - - if(*current_position > MAX_LINE) { - if((is_negative_start && value > 0) || (!is_negative_start && value < 0)) { - // Align values - continue; - } - - if(stream_write_format(file, "\n%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY) == 0) { - FURI_LOG_E(TAG, "Can't write new line!"); - result = false; - break; - } - *current_position = 0; - } - } while(true); - - *is_negative_start = value < 0; - - return result; -} - -bool subghz_history_tmp_write_file_split( - SubGhzHistory* instance, - void* current_item, - const char* dir_path) { - furi_assert(instance); - furi_assert(current_item); - furi_assert(dir_path); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Save temp file splitted: %s", dir_path); -#endif - SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item; - - uint8_t buffer[buffer_size]; - Stream* src = flipper_format_get_raw_stream(item->flipper_string); - stream_rewind(src); - - FlipperFormat* flipper_format_file = flipper_format_file_alloc(instance->storage); - bool result = false; - FuriString* temp_str = furi_string_alloc(); - - do { - if(storage_file_exists(instance->storage, dir_path) && - storage_common_remove(instance->storage, dir_path) != FSE_OK) { - FURI_LOG_E(TAG, "Can't delete old file!"); - break; - } - path_extract_dirname(dir_path, temp_str); - FS_Error fs_result = - storage_common_mkdir(instance->storage, furi_string_get_cstr(temp_str)); - if(fs_result != FSE_OK && fs_result != FSE_EXIST) { - FURI_LOG_E(TAG, "Can't create dir!"); - break; - } - result = flipper_format_file_open_always(flipper_format_file, dir_path); - if(!result) { - FURI_LOG_E(TAG, "Can't open file for write!"); - break; - } - Stream* file = flipper_format_get_raw_stream(flipper_format_file); - - if(!subghz_history_stream_seek_to_key(src, SUBGHZ_HISTORY_TMP_RAW_KEY, false)) { - FURI_LOG_E(TAG, "Can't find key!"); - break; - } - bool is_negative_start = false; - bool found = false; - - size_t offset_start; - offset_start = stream_tell(src); - - // Check for negative value at the start and end to align file by correct values - size_t was_read = stream_read(src, buffer, 1); - if(was_read <= 0) { - FURI_LOG_E(TAG, "Can't obtain first mark!"); - break; - } - - is_negative_start = buffer[0] == '-'; - - // Ready to write stream to file - size_t current_position; - stream_rewind(src); - current_position = stream_copy(src, file, offset_start); - if(current_position != offset_start) { - FURI_LOG_E(TAG, "Invalid copy header data from one stream to another!"); - break; - } - - found = true; - - current_position = 0; - if(!subghz_history_write_file_noise(file, is_negative_start, current_position, false)) { - FURI_LOG_E(TAG, "Add start noise failed!"); - break; - } - - if(stream_write_format(file, "%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY) == 0) { - FURI_LOG_E(TAG, "Can't write new line!"); - result = false; - break; - } - - if(!subghz_history_write_file_data(src, file, &is_negative_start, ¤t_position)) { - FURI_LOG_E(TAG, "Split by lines failed!"); - break; - } - - if(!subghz_history_write_file_noise(file, is_negative_start, current_position, false)) { - FURI_LOG_E(TAG, "Add end noise failed!"); - break; - } - - if(!subghz_history_write_file_noise(file, is_negative_start, 0, true)) { - FURI_LOG_E(TAG, "Add end noise failed!"); - break; - } - - result = found; - } while(false); - flipper_format_file_close(flipper_format_file); - flipper_format_free(flipper_format_file); - furi_string_free(temp_str); - - item->is_file = result; - - return result; -} - -void subghz_history_tmp_write_file_full( - SubGhzHistory* instance, - void* current_item, - FuriString* dir_path) { - SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item; -#ifdef FURI_DEBUG - FURI_LOG_W(TAG, "Save temp file full: %s", furi_string_get_cstr(dir_path)); -#endif - Stream* dst = flipper_format_get_raw_stream(item->flipper_string); - stream_rewind(dst); - if(stream_save_to_file( - dst, instance->storage, furi_string_get_cstr(dir_path), FSOM_CREATE_ALWAYS) > 0) { -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Save done!"); -#endif - // This item contains fake data to load from SD - item->is_file = true; - } else { - FURI_LOG_E(TAG, "Stream copy failed!"); - } -} \ No newline at end of file diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index ee1ee1a4d..607dbeae2 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -110,10 +110,3 @@ bool subghz_history_add_to_history( * @return SubGhzProtocolCommonLoad* */ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx); - -/** Set hopper state for internal usage in history - * - * @param instance - SubGhzHistory instance - * @param hopper_state - bool is hopper running? - */ -void subghz_history_set_hopper_state(SubGhzHistory* instance, bool hopper_state); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index bac25759a..1fbe662ed 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -30,12 +30,6 @@ void subghz_preset_init( subghz->txrx->preset->frequency = frequency; subghz->txrx->preset->data = preset_data; subghz->txrx->preset->data_size = preset_data_size; - - subghz->txrx->raw_bandwidth = - subghz_preset_custom_get_bandwidth(preset_data, preset_data_size); - subghz->txrx->raw_manchester_enabled = - subghz_preset_custom_get_machester_enable(preset_data, preset_data_size); - subghz->txrx->raw_datarate = subghz_preset_custom_get_datarate(preset_data, preset_data_size); } bool subghz_set_preset(SubGhz* subghz, const char* preset) { @@ -75,7 +69,7 @@ void subghz_begin(SubGhz* subghz, uint8_t* preset_data) { furi_hal_subghz_reset(); furi_hal_subghz_idle(); furi_hal_subghz_load_custom_preset(preset_data); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; } @@ -90,7 +84,7 @@ uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { furi_hal_subghz_idle(); uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_flush_rx(); subghz_speaker_on(subghz); furi_hal_subghz_rx(); @@ -109,8 +103,9 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_write(&gpio_cc1101_g0, false); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); subghz_speaker_on(subghz); bool ret = furi_hal_subghz_tx(); subghz->txrx->txrx_state = SubGhzTxRxStateTx; @@ -602,9 +597,15 @@ void subghz_hopper_update(SubGhz* subghz) { } void subghz_speaker_on(SubGhz* subghz) { + if(subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7); + } + if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_acquire(30)) { - furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + if(!subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + } } else { subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; } @@ -612,9 +613,14 @@ void subghz_speaker_on(SubGhz* subghz) { } void subghz_speaker_off(SubGhz* subghz) { + if(subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(NULL); + } if(subghz->txrx->speaker_state != SubGhzSpeakerStateDisable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(NULL); + if(!subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(NULL); + } furi_hal_speaker_release(); if(subghz->txrx->speaker_state == SubGhzSpeakerStateShutdown) subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; @@ -623,17 +629,27 @@ void subghz_speaker_off(SubGhz* subghz) { } void subghz_speaker_mute(SubGhz* subghz) { + if(subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(NULL); + } if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(NULL); + if(!subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(NULL); + } } } } void subghz_speaker_unmute(SubGhz* subghz) { + if(subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7); + } if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + if(!subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + } } } } diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index a6c96cb69..393dd667d 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -3,7 +3,6 @@ #include "helpers/subghz_types.h" #include "helpers/subghz_error_type.h" #include -#include #include "subghz.h" #include "views/receiver.h" #include "views/transmitter.h" @@ -63,6 +62,7 @@ struct SubGhzTxRx { SubGhzEnvironment* environment; SubGhzReceiver* receiver; SubGhzTransmitter* transmitter; + SubGhzProtocolFlag filter; SubGhzProtocolDecoderBase* decoder_result; FlipperFormat* fff_data; SecureData* secure_data; @@ -77,15 +77,10 @@ struct SubGhzTxRx { uint8_t hopper_idx_frequency; SubGhzRxKeyState rx_key_state; + bool debug_pin_state; + float raw_threshold_rssi; uint8_t raw_threshold_rssi_low_count; - - // one of the 16 possible bandwidth values - uint8_t raw_bandwidth; - // datarate in bauds - float raw_datarate; - // flag if manchester encoding/decoding enabled - bool raw_manchester_enabled; }; typedef struct SubGhzTxRx SubGhzTxRx; @@ -114,13 +109,6 @@ struct SubGhz { SubGhzViewTransmitter* subghz_transmitter; VariableItemList* variable_item_list; - // Advanced config items - VariableItem* variable_item_bandwidth; // specific config list view item: bandwidth - VariableItem* variable_item_datarate; // specific config list view item: data rate - VariableItem* variable_item_manchester; // specific config list view item: manchester enc flag - // Advanced config strings - char datarate_input_str[16]; - SubGhzFrequencyAnalyzer* subghz_frequency_analyzer; SubGhzReadRAW* subghz_read_raw; bool raw_send_only; diff --git a/applications/main/subghz/subghz_keystore.c b/applications/main/subghz/subghz_keystore.c new file mode 100644 index 000000000..e0b1cf6ca --- /dev/null +++ b/applications/main/subghz/subghz_keystore.c @@ -0,0 +1,613 @@ +#include "subghz_keystore.h" + +#include +#include + +#include +#include +#include +#include +#include + +#define TAG "SubGhzKeystore" + +#define FILE_BUFFER_SIZE 64 + +#define SUBGHZ_KEYSTORE_FILE_TYPE "Flipper SubGhz Keystore File" +#define SUBGHZ_KEYSTORE_FILE_RAW_TYPE "Flipper SubGhz Keystore RAW File" +#define SUBGHZ_KEYSTORE_FILE_VERSION 0 + +#define SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT 1 +#define SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE 512 +#define SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE (SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE * 2) + +typedef enum { + SubGhzKeystoreEncryptionNone, + SubGhzKeystoreEncryptionAES256, +} SubGhzKeystoreEncryption; + +struct SubGhzKeystore { + SubGhzKeyArray_t data; +}; + +SubGhzKeystore* subghz_keystore_alloc() { + SubGhzKeystore* instance = malloc(sizeof(SubGhzKeystore)); + + SubGhzKeyArray_init(instance->data); + + return instance; +} + +void subghz_keystore_free(SubGhzKeystore* instance) { + furi_assert(instance); + + for + M_EACH(manufacture_code, instance->data, SubGhzKeyArray_t) { + furi_string_free(manufacture_code->name); + manufacture_code->key = 0; + } + SubGhzKeyArray_clear(instance->data); + + free(instance); +} + +static void subghz_keystore_add_key( + SubGhzKeystore* instance, + const char* name, + uint64_t key, + uint16_t type) { + SubGhzKey* manufacture_code = SubGhzKeyArray_push_raw(instance->data); + manufacture_code->name = furi_string_alloc_set(name); + manufacture_code->key = key; + manufacture_code->type = type; +} + +static bool subghz_keystore_process_line(SubGhzKeystore* instance, char* line) { + uint64_t key = 0; + uint16_t type = 0; + char skey[17] = {0}; + char name[65] = {0}; + int ret = sscanf(line, "%16s:%hu:%64s", skey, &type, name); + key = strtoull(skey, NULL, 16); + if(ret == 3) { + subghz_keystore_add_key(instance, name, key, type); + return true; + } else { + FURI_LOG_E(TAG, "Failed to load line: %s\r\n", line); + return false; + } +} + +static void subghz_keystore_mess_with_iv(uint8_t* iv) { + // Alignment check for `ldrd` instruction + furi_assert(((uint32_t)iv) % 4 == 0); + // Please do not share decrypted manufacture keys + // Sharing them will bring some discomfort to legal owners + // And potential legal action against you + // While you reading this code think about your own personal responsibility + asm volatile("nani%=: \n" + "ldrd r0, r2, [%0, #0x0] \n" + "lsl r1, r0, #8 \n" + "lsl r3, r2, #8 \n" + "orr r3, r3, r0, lsr #24\n" + "uadd8 r1, r1, r0 \n" + "uadd8 r3, r3, r2 \n" + "strd r1, r3, [%0, #0x0] \n" + "ldrd r1, r3, [%0, #0x8] \n" + "lsl r0, r1, #8 \n" + "orr r0, r0, r2, lsr #24\n" + "lsl r2, r3, #8 \n" + "orr r2, r2, r1, lsr #24\n" + "uadd8 r1, r1, r0 \n" + "uadd8 r3, r3, r2 \n" + "strd r1, r3, [%0, #0x8] \n" + : + : "r"(iv) + : "r0", "r1", "r2", "r3", "memory"); +} + +static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, uint8_t* iv) { + bool result = true; + uint8_t buffer[FILE_BUFFER_SIZE]; + + char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + size_t encrypted_line_cursor = 0; + + do { + if(iv) { + if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + FURI_LOG_E(TAG, "Unable to load decryption key"); + break; + } + } + + size_t ret = 0; + do { + ret = stream_read(stream, buffer, FILE_BUFFER_SIZE); + for(uint16_t i = 0; i < ret; i++) { + if(buffer[i] == '\n' && encrypted_line_cursor > 0) { + // Process line + if(iv) { + // Data alignment check, 32 instead of 16 because of hex encoding + size_t len = strlen(encrypted_line); + if(len % 32 == 0) { + // Inplace hex to bin conversion + for(size_t i = 0; i < len; i += 2) { + uint8_t hi_nibble = 0; + uint8_t lo_nibble = 0; + hex_char_to_hex_nibble(encrypted_line[i], &hi_nibble); + hex_char_to_hex_nibble(encrypted_line[i + 1], &lo_nibble); + encrypted_line[i / 2] = (hi_nibble << 4) | lo_nibble; + } + len /= 2; + + if(furi_hal_crypto_decrypt( + (uint8_t*)encrypted_line, (uint8_t*)decrypted_line, len)) { + subghz_keystore_process_line(instance, decrypted_line); + } else { + FURI_LOG_E(TAG, "Decryption failed"); + result = false; + break; + } + } else { + FURI_LOG_E(TAG, "Invalid encrypted data: %s", encrypted_line); + } + } else { + subghz_keystore_process_line(instance, encrypted_line); + } + // reset line buffer + memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + encrypted_line_cursor = 0; + } else if(buffer[i] == '\r' || buffer[i] == '\n') { + // do not add line endings to the buffer + } else { + if(encrypted_line_cursor < SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE) { + encrypted_line[encrypted_line_cursor] = buffer[i]; + encrypted_line_cursor++; + } else { + FURI_LOG_E(TAG, "Malformed file"); + result = false; + break; + } + } + } + } while(ret > 0 && result); + + if(iv) furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + } while(false); + + free(encrypted_line); + free(decrypted_line); + + return result; +} + +bool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) { + furi_assert(instance); + bool result = false; + uint8_t iv[16]; + uint32_t version; + uint32_t encryption; + + FuriString* filetype; + filetype = furi_string_alloc(); + + FURI_LOG_I(TAG, "Loading keystore %s", file_name); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + do { + if(!flipper_format_file_open_existing(flipper_format, file_name)) { + FURI_LOG_E(TAG, "Unable to open file for read: %s", file_name); + break; + } + if(!flipper_format_read_header(flipper_format, filetype, &version)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Encryption", (uint32_t*)&encryption, 1)) { + FURI_LOG_E(TAG, "Missing encryption type"); + break; + } + + if(strcmp(furi_string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_TYPE) != 0 || + version != SUBGHZ_KEYSTORE_FILE_VERSION) { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + Stream* stream = flipper_format_get_raw_stream(flipper_format); + if(encryption == SubGhzKeystoreEncryptionNone) { + result = subghz_keystore_read_file(instance, stream, NULL); + } else if(encryption == SubGhzKeystoreEncryptionAES256) { + if(!flipper_format_read_hex(flipper_format, "IV", iv, 16)) { + FURI_LOG_E(TAG, "Missing IV"); + break; + } + subghz_keystore_mess_with_iv(iv); + result = subghz_keystore_read_file(instance, stream, iv); + } else { + FURI_LOG_E(TAG, "Unknown encryption"); + break; + } + } while(0); + flipper_format_free(flipper_format); + + furi_record_close(RECORD_STORAGE); + + furi_string_free(filetype); + + return result; +} + +bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8_t* iv) { + furi_assert(instance); + bool result = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + do { + if(!flipper_format_file_open_always(flipper_format, file_name)) { + FURI_LOG_E(TAG, "Unable to open file for write: %s", file_name); + break; + } + if(!flipper_format_write_header_cstr( + flipper_format, SUBGHZ_KEYSTORE_FILE_TYPE, SUBGHZ_KEYSTORE_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + uint32_t encryption = SubGhzKeystoreEncryptionAES256; + if(!flipper_format_write_uint32(flipper_format, "Encryption", &encryption, 1)) { + FURI_LOG_E(TAG, "Unable to add Encryption"); + break; + } + if(!flipper_format_write_hex(flipper_format, "IV", iv, 16)) { + FURI_LOG_E(TAG, "Unable to add IV"); + break; + } + + subghz_keystore_mess_with_iv(iv); + + if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + FURI_LOG_E(TAG, "Unable to load encryption key"); + break; + } + + Stream* stream = flipper_format_get_raw_stream(flipper_format); + size_t encrypted_line_count = 0; + for + M_EACH(key, instance->data, SubGhzKeyArray_t) { + // Wipe buffer before packing + memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + // Form unecreypted line + int len = snprintf( + decrypted_line, + SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE, + "%08lX%08lX:%hu:%s", + (uint32_t)(key->key >> 32), + (uint32_t)key->key, + key->type, + furi_string_get_cstr(key->name)); + // Verify length and align + furi_assert(len > 0); + if(len % 16 != 0) { + len += (16 - len % 16); + } + furi_assert(len % 16 == 0); + furi_assert(len <= SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + // Form encrypted line + if(!furi_hal_crypto_encrypt( + (uint8_t*)decrypted_line, (uint8_t*)encrypted_line, len)) { + FURI_LOG_E(TAG, "Encryption failed"); + break; + } + // HEX Encode encrypted line + const char xx[] = "0123456789ABCDEF"; + for(int i = 0; i < len; i++) { + size_t cursor = len - i - 1; + size_t hex_cursor = len * 2 - i * 2 - 1; + encrypted_line[hex_cursor] = xx[encrypted_line[cursor] & 0xF]; + encrypted_line[hex_cursor - 1] = xx[(encrypted_line[cursor] >> 4) & 0xF]; + } + stream_write_cstring(stream, encrypted_line); + stream_write_char(stream, '\n'); + encrypted_line_count++; + } + furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + size_t total_keys = SubGhzKeyArray_size(instance->data); + result = encrypted_line_count == total_keys; + if(result) { + FURI_LOG_I(TAG, "Success. Encrypted: %zu of %zu", encrypted_line_count, total_keys); + } else { + FURI_LOG_E(TAG, "Failure. Encrypted: %zu of %zu", encrypted_line_count, total_keys); + } + } while(0); + flipper_format_free(flipper_format); + + free(encrypted_line); + free(decrypted_line); + furi_record_close(RECORD_STORAGE); + + return result; +} + +SubGhzKeyArray_t* subghz_keystore_get_data(SubGhzKeystore* instance) { + furi_assert(instance); + return &instance->data; +} + +bool subghz_keystore_raw_encrypted_save( + const char* input_file_name, + const char* output_file_name, + uint8_t* iv) { + bool encrypted = false; + uint32_t version; + uint32_t encryption; + FuriString* filetype; + filetype = furi_string_alloc(); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + + FlipperFormat* input_flipper_format = flipper_format_file_alloc(storage); + do { + if(!flipper_format_file_open_existing(input_flipper_format, input_file_name)) { + FURI_LOG_E(TAG, "Unable to open file for read: %s", input_file_name); + break; + } + if(!flipper_format_read_header(input_flipper_format, filetype, &version)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + if(!flipper_format_read_uint32( + input_flipper_format, "Encryption", (uint32_t*)&encryption, 1)) { + FURI_LOG_E(TAG, "Missing encryption type"); + break; + } + + if(strcmp(furi_string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 || + version != SUBGHZ_KEYSTORE_FILE_VERSION) { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + if(encryption != SubGhzKeystoreEncryptionNone) { + FURI_LOG_E(TAG, "Already encryption"); + break; + } + Stream* input_stream = flipper_format_get_raw_stream(input_flipper_format); + + FlipperFormat* output_flipper_format = flipper_format_file_alloc(storage); + + if(!flipper_format_file_open_always(output_flipper_format, output_file_name)) { + FURI_LOG_E(TAG, "Unable to open file for write: %s", output_file_name); + break; + } + if(!flipper_format_write_header_cstr( + output_flipper_format, + furi_string_get_cstr(filetype), + SUBGHZ_KEYSTORE_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + uint32_t encryption = SubGhzKeystoreEncryptionAES256; + if(!flipper_format_write_uint32(output_flipper_format, "Encryption", &encryption, 1)) { + FURI_LOG_E(TAG, "Unable to add Encryption"); + break; + } + if(!flipper_format_write_hex(output_flipper_format, "IV", iv, 16)) { + FURI_LOG_E(TAG, "Unable to add IV"); + break; + } + + if(!flipper_format_write_string_cstr(output_flipper_format, "Encrypt_data", "RAW")) { + FURI_LOG_E(TAG, "Unable to add Encrypt_data"); + break; + } + + subghz_keystore_mess_with_iv(iv); + + if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + FURI_LOG_E(TAG, "Unable to load encryption key"); + break; + } + + Stream* output_stream = flipper_format_get_raw_stream(output_flipper_format); + uint8_t buffer[FILE_BUFFER_SIZE]; + bool result = true; + + size_t ret = 0; + furi_assert(FILE_BUFFER_SIZE % 16 == 0); + + //skip the end of the previous line "\n" + stream_read(input_stream, buffer, 1); + + do { + memset(buffer, 0, FILE_BUFFER_SIZE); + ret = stream_read(input_stream, buffer, FILE_BUFFER_SIZE); + if(ret == 0) { + break; + } + + for(uint16_t i = 0; i < FILE_BUFFER_SIZE - 1; i += 2) { + uint8_t hi_nibble = 0; + uint8_t lo_nibble = 0; + hex_char_to_hex_nibble(buffer[i], &hi_nibble); + hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble); + buffer[i / 2] = (hi_nibble << 4) | lo_nibble; + } + + memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + // Form encrypted line + if(!furi_hal_crypto_encrypt( + (uint8_t*)buffer, (uint8_t*)encrypted_line, FILE_BUFFER_SIZE / 2)) { + FURI_LOG_E(TAG, "Encryption failed"); + result = false; + break; + } + + // HEX Encode encrypted line + const char xx[] = "0123456789ABCDEF"; + for(size_t i = 0; i < FILE_BUFFER_SIZE / 2; i++) { + size_t cursor = FILE_BUFFER_SIZE / 2 - i - 1; + size_t hex_cursor = FILE_BUFFER_SIZE - i * 2 - 1; + encrypted_line[hex_cursor] = xx[encrypted_line[cursor] & 0xF]; + encrypted_line[hex_cursor - 1] = xx[(encrypted_line[cursor] >> 4) & 0xF]; + } + stream_write_cstring(output_stream, encrypted_line); + + } while(true); + + flipper_format_free(output_flipper_format); + + furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + + if(!result) break; + + encrypted = true; + } while(0); + + flipper_format_free(input_flipper_format); + + free(encrypted_line); + + furi_record_close(RECORD_STORAGE); + + return encrypted; +} + +bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* data, size_t len) { + bool result = false; + uint8_t iv[16]; + uint32_t version; + uint32_t encryption; + + FuriString* str_temp; + str_temp = furi_string_alloc(); + + Storage* storage = furi_record_open(RECORD_STORAGE); + char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + do { + if(!flipper_format_file_open_existing(flipper_format, file_name)) { + FURI_LOG_E(TAG, "Unable to open file for read: %s", file_name); + break; + } + if(!flipper_format_read_header(flipper_format, str_temp, &version)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Encryption", (uint32_t*)&encryption, 1)) { + FURI_LOG_E(TAG, "Missing encryption type"); + break; + } + + if(strcmp(furi_string_get_cstr(str_temp), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 || + version != SUBGHZ_KEYSTORE_FILE_VERSION) { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + Stream* stream = flipper_format_get_raw_stream(flipper_format); + if(encryption != SubGhzKeystoreEncryptionAES256) { + FURI_LOG_E(TAG, "Unknown encryption"); + break; + } + + if(offset < 16) { + if(!flipper_format_read_hex(flipper_format, "IV", iv, 16)) { + FURI_LOG_E(TAG, "Missing IV"); + break; + } + subghz_keystore_mess_with_iv(iv); + } + + if(!flipper_format_read_string(flipper_format, "Encrypt_data", str_temp)) { + FURI_LOG_E(TAG, "Missing Encrypt_data"); + break; + } + + size_t bufer_size; + if(len <= (16 - offset % 16)) { + bufer_size = 32; + } else { + bufer_size = (((len) / 16) + 2) * 32; + } + furi_assert(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE >= bufer_size / 2); + + uint8_t buffer[bufer_size]; + size_t ret = 0; + bool decrypted = true; + //skip the end of the previous line "\n" + stream_read(stream, buffer, 1); + + size_t size = stream_size(stream); + size -= stream_tell(stream); + if(size < (offset * 2 + len * 2)) { + FURI_LOG_E(TAG, "Seek position exceeds file size"); + break; + } + + if(offset >= 16) { + stream_seek(stream, ((offset / 16) - 1) * 32, StreamOffsetFromCurrent); + ret = stream_read(stream, buffer, 32); + furi_assert(ret == 32); + for(uint16_t i = 0; i < ret - 1; i += 2) { + uint8_t hi_nibble = 0; + uint8_t lo_nibble = 0; + hex_char_to_hex_nibble(buffer[i], &hi_nibble); + hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble); + iv[i / 2] = (hi_nibble << 4) | lo_nibble; + } + } + + if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + FURI_LOG_E(TAG, "Unable to load encryption key"); + break; + } + + do { + memset(buffer, 0, bufer_size); + ret = stream_read(stream, buffer, bufer_size); + furi_assert(ret == bufer_size); + for(uint16_t i = 0; i < ret - 1; i += 2) { + uint8_t hi_nibble = 0; + uint8_t lo_nibble = 0; + hex_char_to_hex_nibble(buffer[i], &hi_nibble); + hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble); + buffer[i / 2] = (hi_nibble << 4) | lo_nibble; + } + + memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + + if(!furi_hal_crypto_decrypt( + (uint8_t*)buffer, (uint8_t*)decrypted_line, bufer_size / 2)) { + decrypted = false; + FURI_LOG_E(TAG, "Decryption failed"); + break; + } + memcpy(data, (uint8_t*)decrypted_line + (offset - (offset / 16) * 16), len); + + } while(0); + furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + if(decrypted) result = true; + } while(0); + flipper_format_free(flipper_format); + + furi_record_close(RECORD_STORAGE); + + free(decrypted_line); + + furi_string_free(str_temp); + + return result; +} diff --git a/applications/main/subghz/subghz_keystore.h b/applications/main/subghz/subghz_keystore.h new file mode 100644 index 000000000..06ae8adae --- /dev/null +++ b/applications/main/subghz/subghz_keystore.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + FuriString* name; + uint64_t key; + uint16_t type; +} SubGhzKey; + +ARRAY_DEF(SubGhzKeyArray, SubGhzKey, M_POD_OPLIST) + +#define M_OPL_SubGhzKeyArray_t() ARRAY_OPLIST(SubGhzKeyArray, M_POD_OPLIST) + +typedef struct SubGhzKeystore SubGhzKeystore; + +/** + * Allocate SubGhzKeystore. + * @return SubGhzKeystore* pointer to a SubGhzKeystore instance + */ +SubGhzKeystore* subghz_keystore_alloc(); + +/** + * Free SubGhzKeystore. + * @param instance Pointer to a SubGhzKeystore instance + */ +void subghz_keystore_free(SubGhzKeystore* instance); + +/** + * Loading manufacture key from file + * @param instance Pointer to a SubGhzKeystore instance + * @param filename Full path to the file + */ +bool subghz_keystore_load(SubGhzKeystore* instance, const char* filename); + +/** + * Save manufacture key to file + * @param instance Pointer to a SubGhzKeystore instance + * @param filename Full path to the file + * @return true On success + */ +bool subghz_keystore_save(SubGhzKeystore* instance, const char* filename, uint8_t* iv); + +/** + * Get array of keys and names manufacture + * @param instance Pointer to a SubGhzKeystore instance + * @return SubGhzKeyArray_t* + */ +SubGhzKeyArray_t* subghz_keystore_get_data(SubGhzKeystore* instance); + +/** + * Save RAW encrypted to file + * @param input_file_name Full path to the input file + * @param output_file_name Full path to the output file + * @param iv IV, 16 bytes in hex + */ +bool subghz_keystore_raw_encrypted_save( + const char* input_file_name, + const char* output_file_name, + uint8_t* iv); + +/** + * Get decrypt RAW data to file + * @param file_name Full path to the input file + * @param offset Offset from the start of the RAW data + * @param data Returned array + * @param len Required data length + * @return true On success + */ +bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* data, size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 478e32347..8e7016df7 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -1,8 +1,5 @@ #include "subghz_last_settings.h" #include "subghz_i.h" -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING -#include -#endif #define TAG "SubGhzLastSettings" @@ -16,11 +13,6 @@ #define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2 #define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER -93.0f -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING -#define SUBGHZ_LAST_SETTING_DEFAULT_READ_RAW 0 -#define SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW "DetectRaw" -#endif - #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY "Frequency" //#define SUBGHZ_LAST_SETTING_FIELD_PRESET "Preset" #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL "FeedbackLevel" @@ -52,9 +44,6 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count //int32_t temp_preset = 0; bool frequency_analyzer_feedback_level_was_read = false; bool frequency_analyzer_trigger_was_read = false; -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - uint32_t temp_read_raw = 0; -#endif if(FSE_OK == storage_sd_status(storage) && SUBGHZ_LAST_SETTINGS_PATH && flipper_format_file_open_existing(fff_data_file, SUBGHZ_LAST_SETTINGS_PATH)) { @@ -73,10 +62,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER, (float*)&temp_frequency_analyzer_trigger, 1); -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - flipper_format_read_uint32( - fff_data_file, SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW, (uint32_t*)&temp_read_raw, 1); -#endif + } else { FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); } @@ -88,9 +74,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->frequency_analyzer_feedback_level = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL; instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - instance->detect_raw = SUBGHZ_LAST_SETTING_DEFAULT_READ_RAW; -#endif + } else { instance->frequency = temp_frequency; instance->frequency_analyzer_feedback_level = @@ -101,9 +85,6 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->frequency_analyzer_trigger = frequency_analyzer_trigger_was_read ? temp_frequency_analyzer_trigger : SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - instance->detect_raw = temp_read_raw; -#endif /*if(temp_preset > (int32_t)preset_count - 1 || temp_preset < 0) { FURI_LOG_W(TAG, "Last used preset no found");*/ @@ -164,12 +145,6 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { 1)) { break; } -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - if(!flipper_format_insert_or_update_uint32( - file, SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW, &instance->detect_raw, 1)) { - break; - } -#endif saved = true; } while(0); @@ -183,17 +158,3 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { return saved; } - -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING -void subghz_last_settings_set_detect_raw_values(void* context) { - furi_assert(context); - SubGhz* instance = (SubGhz*)context; - bool is_detect_raw = instance->last_settings->detect_raw > 0; - subghz_receiver_set_filter( - instance->txrx->receiver, is_detect_raw ? DETECT_RAW_TRUE : DETECT_RAW_FALSE); - subghz_protocol_decoder_raw_set_auto_mode( - subghz_receiver_search_decoder_base_by_name( - instance->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME), - is_detect_raw); -} -#endif \ No newline at end of file diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index ef3674d69..f08d99c81 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -1,23 +1,12 @@ #pragma once -// Enable saving detect raw setting state -// #define SUBGHZ_SAVE_DETECT_RAW_SETTING 1 - #include #include #include #include -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING -#include -#define DETECT_RAW_FALSE SubGhzProtocolFlag_Decodable -#define DETECT_RAW_TRUE SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_RAW -#endif typedef struct { uint32_t frequency; -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - uint32_t detect_raw; -#endif int32_t preset; uint32_t frequency_analyzer_feedback_level; float frequency_analyzer_trigger; @@ -30,6 +19,3 @@ void subghz_last_settings_free(SubGhzLastSettings* instance); void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count); bool subghz_last_settings_save(SubGhzLastSettings* instance); -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING -void subghz_last_settings_set_detect_raw_values(void* context); -#endif \ No newline at end of file diff --git a/applications/main/subghz/subghz_setting.c b/applications/main/subghz/subghz_setting.c new file mode 100644 index 000000000..35ba54a8a --- /dev/null +++ b/applications/main/subghz/subghz_setting.c @@ -0,0 +1,480 @@ +#include "subghz_setting.h" +#include "types.h" +//#include "subghz_i.h" + +#include +#include +#include + +#define TAG "SubGhzSetting" + +#define SUBGHZ_SETTING_FILE_TYPE "Flipper SubGhz Setting File" +#define SUBGHZ_SETTING_FILE_VERSION 1 + +#define FREQUENCY_FLAG_DEFAULT (1 << 31) +#define FREQUENCY_MASK (0xFFFFFFFF ^ FREQUENCY_FLAG_DEFAULT) + +/* Default */ +static const uint32_t subghz_frequency_list[] = { + /* 300 - 348 */ + 300000000, + 302757000, + 303875000, + 304250000, + 307000000, + 307500000, + 307800000, + 309000000, + 310000000, + 312000000, + 312100000, + 312200000, + 313000000, + 313850000, + 314000000, + 314350000, + 314980000, + 315000000, + 318000000, + 330000000, + 345000000, + 348000000, + 350000000, + + /* 387 - 464 */ + 387000000, + 390000000, + 418000000, + 433075000, /* LPD433 first */ + 433220000, + 433420000, + 433657070, + 433889000, + 433920000 | FREQUENCY_FLAG_DEFAULT, /* LPD433 mid */ + 434075000, + 434176948, + 434190000, + 434390000, + 434420000, + 434620000, + 434775000, /* LPD433 last channels */ + 438900000, + 440175000, + 464000000, + + /* 779 - 928 */ + 779000000, + 868350000, + 868400000, + 868800000, + 868950000, + 906400000, + 915000000, + 925000000, + 928000000, + 0, +}; + +static const uint32_t subghz_hopper_frequency_list[] = { + 315000000, + 330000000, + 390000000, + 433420000, + 433920000, + 868350000, + 0, +}; + +typedef struct { + FuriString* custom_preset_name; + uint8_t* custom_preset_data; + size_t custom_preset_data_size; +} SubGhzSettingCustomPresetItem; + +ARRAY_DEF(SubGhzSettingCustomPresetItemArray, SubGhzSettingCustomPresetItem, M_POD_OPLIST) + +#define M_OPL_SubGhzSettingCustomPresetItemArray_t() \ + ARRAY_OPLIST(SubGhzSettingCustomPresetItemArray, M_POD_OPLIST) + +LIST_DEF(FrequencyList, uint32_t) + +#define M_OPL_FrequencyList_t() LIST_OPLIST(FrequencyList) + +typedef struct { + SubGhzSettingCustomPresetItemArray_t data; +} SubGhzSettingCustomPresetStruct; + +struct SubGhzSetting { + FrequencyList_t frequencies; + FrequencyList_t hopper_frequencies; + SubGhzSettingCustomPresetStruct* preset; +}; + +SubGhzSetting* subghz_setting_alloc(void) { + SubGhzSetting* instance = malloc(sizeof(SubGhzSetting)); + FrequencyList_init(instance->frequencies); + FrequencyList_init(instance->hopper_frequencies); + instance->preset = malloc(sizeof(SubGhzSettingCustomPresetStruct)); + SubGhzSettingCustomPresetItemArray_init(instance->preset->data); + return instance; +} + +static void subghz_setting_preset_reset(SubGhzSetting* instance) { + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + furi_string_free(item->custom_preset_name); + free(item->custom_preset_data); + } + SubGhzSettingCustomPresetItemArray_reset(instance->preset->data); +} + +void subghz_setting_free(SubGhzSetting* instance) { + furi_assert(instance); + FrequencyList_clear(instance->frequencies); + FrequencyList_clear(instance->hopper_frequencies); + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + furi_string_free(item->custom_preset_name); + free(item->custom_preset_data); + } + SubGhzSettingCustomPresetItemArray_clear(instance->preset->data); + free(instance->preset); + free(instance); +} + +static void subghz_setting_load_default_preset( + SubGhzSetting* instance, + const char* preset_name, + const uint8_t* preset_data, + const uint8_t preset_pa_table[8]) { + furi_assert(instance); + furi_assert(preset_data); + uint32_t preset_data_count = 0; + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data); + + item->custom_preset_name = furi_string_alloc(); + furi_string_set(item->custom_preset_name, preset_name); + + while(preset_data[preset_data_count]) { + preset_data_count += 2; + } + preset_data_count += 2; + item->custom_preset_data_size = sizeof(uint8_t) * preset_data_count + sizeof(uint8_t) * 8; + item->custom_preset_data = malloc(item->custom_preset_data_size); + //load preset register + memcpy(&item->custom_preset_data[0], &preset_data[0], preset_data_count); + //load pa table + memcpy(&item->custom_preset_data[preset_data_count], &preset_pa_table[0], 8); +} + +static void subghz_setting_load_default_region( + SubGhzSetting* instance, + const uint32_t frequencies[], + const uint32_t hopper_frequencies[]) { + furi_assert(instance); + + FrequencyList_reset(instance->frequencies); + FrequencyList_reset(instance->hopper_frequencies); + subghz_setting_preset_reset(instance); + + while(*frequencies) { + FrequencyList_push_back(instance->frequencies, *frequencies); + frequencies++; + } + + while(*hopper_frequencies) { + FrequencyList_push_back(instance->hopper_frequencies, *hopper_frequencies); + hopper_frequencies++; + } + + subghz_setting_load_default_preset( + instance, + "AM270", + (uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs, + furi_hal_subghz_preset_ook_async_patable); + subghz_setting_load_default_preset( + instance, + "AM650", + (uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs, + furi_hal_subghz_preset_ook_async_patable); + subghz_setting_load_default_preset( + instance, + "FM238", + (uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs, + furi_hal_subghz_preset_2fsk_async_patable); + subghz_setting_load_default_preset( + instance, + "FM476", + (uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs, + furi_hal_subghz_preset_2fsk_async_patable); +} + +// Region check removed +void subghz_setting_load_default(SubGhzSetting* instance) { + subghz_setting_load_default_region( + instance, subghz_frequency_list, subghz_hopper_frequency_list); +} + +void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { + furi_assert(instance); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + + FuriString* temp_str; + temp_str = furi_string_alloc(); + uint32_t temp_data32; + bool temp_bool; + + subghz_setting_load_default(instance); + + if(file_path) { + do { + if(!flipper_format_file_open_existing(fff_data_file, file_path)) { + FURI_LOG_I(TAG, "File is not used %s", file_path); + break; + } + + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + + if((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_SETTING_FILE_TYPE)) && + temp_data32 == SUBGHZ_SETTING_FILE_VERSION) { + } else { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + // Standard frequencies (optional) + temp_bool = true; + flipper_format_read_bool(fff_data_file, "Add_standard_frequencies", &temp_bool, 1); + if(!temp_bool) { + FURI_LOG_I(TAG, "Removing standard frequencies"); + FrequencyList_reset(instance->frequencies); + FrequencyList_reset(instance->hopper_frequencies); + } else { + FURI_LOG_I(TAG, "Keeping standard frequencies"); + } + + // Load frequencies + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + while(flipper_format_read_uint32( + fff_data_file, "Frequency", (uint32_t*)&temp_data32, 1)) { + if(furi_hal_subghz_is_frequency_valid(temp_data32)) { + FURI_LOG_I(TAG, "Frequency loaded %lu", temp_data32); + FrequencyList_push_back(instance->frequencies, temp_data32); + } else { + FURI_LOG_E(TAG, "Frequency not supported %lu", temp_data32); + } + } + + // Load hopper frequencies + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + while(flipper_format_read_uint32( + fff_data_file, "Hopper_frequency", (uint32_t*)&temp_data32, 1)) { + if(furi_hal_subghz_is_frequency_valid(temp_data32)) { + FURI_LOG_I(TAG, "Hopper frequency loaded %lu", temp_data32); + FrequencyList_push_back(instance->hopper_frequencies, temp_data32); + } else { + FURI_LOG_E(TAG, "Hopper frequency not supported %lu", temp_data32); + } + } + + // Default frequency (optional) + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(flipper_format_read_uint32(fff_data_file, "Default_frequency", &temp_data32, 1)) { + subghz_setting_set_default_frequency(instance, temp_data32); + } + + // custom preset (optional) + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + while(flipper_format_read_string(fff_data_file, "Custom_preset_name", temp_str)) { + FURI_LOG_I(TAG, "Custom preset loaded %s", furi_string_get_cstr(temp_str)); + subghz_setting_load_custom_preset( + instance, furi_string_get_cstr(temp_str), fff_data_file); + } + + } while(false); + } + + furi_string_free(temp_str); + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); + + if(!FrequencyList_size(instance->frequencies) || + !FrequencyList_size(instance->hopper_frequencies)) { + FURI_LOG_E(TAG, "Error loading user settings, loading default settings"); + subghz_setting_load_default(instance); + } +} + +void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup) { + for + M_EACH(frequency, instance->frequencies, FrequencyList_t) { + *frequency &= FREQUENCY_MASK; + if(*frequency == frequency_to_setup) { + *frequency |= FREQUENCY_FLAG_DEFAULT; + } + } +} + +size_t subghz_setting_get_frequency_count(SubGhzSetting* instance) { + furi_assert(instance); + return FrequencyList_size(instance->frequencies); +} + +size_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance) { + furi_assert(instance); + return FrequencyList_size(instance->hopper_frequencies); +} + +size_t subghz_setting_get_preset_count(SubGhzSetting* instance) { + furi_assert(instance); + return SubGhzSettingCustomPresetItemArray_size(instance->preset->data); +} + +const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + if(idx >= SubGhzSettingCustomPresetItemArray_size(instance->preset->data)) { + idx = 0; + } + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); + return furi_string_get_cstr(item->custom_preset_name); +} + +int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name) { + furi_assert(instance); + size_t idx = 0; + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) { + return idx; + } + idx++; + } + furi_crash("SubGhz: No name preset."); + return -1; +} + +bool subghz_setting_load_custom_preset( + SubGhzSetting* instance, + const char* preset_name, + FlipperFormat* fff_data_file) { + furi_assert(instance); + furi_assert(preset_name); + uint32_t temp_data32; + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data); + item->custom_preset_name = furi_string_alloc(); + furi_string_set(item->custom_preset_name, preset_name); + do { + if(!flipper_format_get_value_count(fff_data_file, "Custom_preset_data", &temp_data32)) + break; + if(!temp_data32 || (temp_data32 % 2)) { + FURI_LOG_E(TAG, "Integrity error Custom_preset_data"); + break; + } + item->custom_preset_data_size = sizeof(uint8_t) * temp_data32; + item->custom_preset_data = malloc(item->custom_preset_data_size); + if(!flipper_format_read_hex( + fff_data_file, + "Custom_preset_data", + item->custom_preset_data, + item->custom_preset_data_size)) { + FURI_LOG_E(TAG, "Missing Custom_preset_data"); + break; + } + return true; + } while(true); + return false; +} + +bool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* preset_name) { + furi_assert(instance); + furi_assert(preset_name); + SubGhzSettingCustomPresetItemArray_it_t it; + SubGhzSettingCustomPresetItemArray_it_last(it, instance->preset->data); + while(!SubGhzSettingCustomPresetItemArray_end_p(it)) { + SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_ref(it); + if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) { + furi_string_free(item->custom_preset_name); + free(item->custom_preset_data); + SubGhzSettingCustomPresetItemArray_remove(instance->preset->data, it); + return true; + } + SubGhzSettingCustomPresetItemArray_previous(it); + } + return false; +} + +uint8_t* subghz_setting_get_preset_data(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); + return item->custom_preset_data; +} + +size_t subghz_setting_get_preset_data_size(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); + return item->custom_preset_data_size; +} + +uint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const char* preset_name) { + furi_assert(instance); + SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_get( + instance->preset->data, subghz_setting_get_inx_preset_by_name(instance, preset_name)); + return item->custom_preset_data; +} + +uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + if(idx < FrequencyList_size(instance->frequencies)) { + return (*FrequencyList_get(instance->frequencies, idx)) & FREQUENCY_MASK; + } else { + return 0; + } +} + +uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + if(idx < FrequencyList_size(instance->frequencies)) { + return *FrequencyList_get(instance->hopper_frequencies, idx); + } else { + return 0; + } +} + +uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance) { + furi_assert(instance); + for(size_t i = 0; i < FrequencyList_size(instance->frequencies); i++) { + uint32_t frequency = *FrequencyList_get(instance->frequencies, i); + if(frequency & FREQUENCY_FLAG_DEFAULT) { + return i; + } + } + return 0; +} + +uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance) { + furi_assert(instance); + return subghz_setting_get_frequency( + instance, subghz_setting_get_frequency_default_index(instance)); +} diff --git a/applications/main/subghz/subghz_setting.h b/applications/main/subghz/subghz_setting.h new file mode 100644 index 000000000..3cb07ff6d --- /dev/null +++ b/applications/main/subghz/subghz_setting.h @@ -0,0 +1,58 @@ + +#pragma once + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4 + +typedef struct SubGhzSetting SubGhzSetting; + +SubGhzSetting* subghz_setting_alloc(void); + +void subghz_setting_free(SubGhzSetting* instance); + +void subghz_setting_load(SubGhzSetting* instance, const char* file_path); + +size_t subghz_setting_get_frequency_count(SubGhzSetting* instance); + +size_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance); + +size_t subghz_setting_get_preset_count(SubGhzSetting* instance); + +const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx); + +int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name); + +uint8_t* subghz_setting_get_preset_data(SubGhzSetting* instance, size_t idx); + +size_t subghz_setting_get_preset_data_size(SubGhzSetting* instance, size_t idx); + +uint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const char* preset_name); + +bool subghz_setting_load_custom_preset( + SubGhzSetting* instance, + const char* preset_name, + FlipperFormat* fff_data_file); + +bool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* preset_name); + +uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx); + +uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx); + +uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance); + +uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance); + +void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/subghz_tx_rx_worker.c b/applications/main/subghz/subghz_tx_rx_worker.c new file mode 100644 index 000000000..e380fc967 --- /dev/null +++ b/applications/main/subghz/subghz_tx_rx_worker.c @@ -0,0 +1,260 @@ +#include "subghz_tx_rx_worker.h" + +#include + +#define TAG "SubGhzTxRxWorker" + +#define SUBGHZ_TXRX_WORKER_BUF_SIZE 2048 +//you can not set more than 62 because it will not fit into the FIFO CC1101 +#define SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE 60 + +#define SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF 40 + +struct SubGhzTxRxWorker { + FuriThread* thread; + FuriStreamBuffer* stream_tx; + FuriStreamBuffer* stream_rx; + + volatile bool worker_running; + volatile bool worker_stoping; + + SubGhzTxRxWorkerStatus status; + + uint32_t frequency; + + SubGhzTxRxWorkerCallbackHaveRead callback_have_read; + void* context_have_read; +}; + +bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { + furi_assert(instance); + bool ret = false; + size_t stream_tx_free_byte = furi_stream_buffer_spaces_available(instance->stream_tx); + if(size && (stream_tx_free_byte >= size)) { + if(furi_stream_buffer_send( + instance->stream_tx, data, size, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF) == + size) { + ret = true; + } + } + return ret; +} + +size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance) { + furi_assert(instance); + return furi_stream_buffer_bytes_available(instance->stream_rx); +} + +size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { + furi_assert(instance); + return furi_stream_buffer_receive(instance->stream_rx, data, size, 0); +} + +void subghz_tx_rx_worker_set_callback_have_read( + SubGhzTxRxWorker* instance, + SubGhzTxRxWorkerCallbackHaveRead callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + furi_assert(context); + instance->callback_have_read = callback; + instance->context_have_read = context; +} + +bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* size) { + uint8_t timeout = 100; + bool ret = false; + if(instance->status != SubGhzTxRxWorkerStatusRx) { + furi_hal_subghz_rx(); + instance->status = SubGhzTxRxWorkerStatusRx; + furi_delay_tick(1); + } + //waiting for reception to complete + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { + furi_delay_tick(1); + if(!--timeout) { + FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); + furi_hal_subghz_flush_rx(); + furi_hal_subghz_rx(); + break; + } + } + + if(furi_hal_subghz_rx_pipe_not_empty()) { + FURI_LOG_I( + TAG, + "RSSI: %03.1fdbm LQI: %d", + (double)furi_hal_subghz_get_rssi(), + furi_hal_subghz_get_lqi()); + if(furi_hal_subghz_is_rx_data_crc_valid()) { + furi_hal_subghz_read_packet(data, size); + ret = true; + } + furi_hal_subghz_flush_rx(); + furi_hal_subghz_rx(); + } + return ret; +} + +void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { + uint8_t timeout = 200; + if(instance->status != SubGhzTxRxWorkerStatusIDLE) { + furi_hal_subghz_idle(); + } + furi_hal_subghz_write_packet(data, size); + furi_hal_subghz_tx(); //start send + instance->status = SubGhzTxRxWorkerStatusTx; + while(!furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be set -> sync transmitted + furi_delay_tick(1); + if(!--timeout) { + FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); + break; + } + } + while(furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be cleared -> end of packet + furi_delay_tick(1); + if(!--timeout) { + FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); + break; + } + } + furi_hal_subghz_idle(); + instance->status = SubGhzTxRxWorkerStatusIDLE; +} +/** Worker thread + * + * @param context + * @return exit code + */ +static int32_t subghz_tx_rx_worker_thread(void* context) { + SubGhzTxRxWorker* instance = context; + FURI_LOG_I(TAG, "Worker start"); + + furi_hal_subghz_reset(); + furi_hal_subghz_idle(); + furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); + //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + + furi_hal_subghz_set_frequency_and_path(instance->frequency); + furi_hal_subghz_flush_rx(); + + uint8_t data[SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE + 1] = {0}; + size_t size_tx = 0; + uint8_t size_rx[1] = {0}; + uint8_t timeout_tx = 0; + bool callback_rx = false; + + while(instance->worker_running) { + //transmit + size_tx = furi_stream_buffer_bytes_available(instance->stream_tx); + if(size_tx > 0 && !timeout_tx) { + timeout_tx = 10; //20ms + if(size_tx > SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE) { + furi_stream_buffer_receive( + instance->stream_tx, + &data, + SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE, + SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); + subghz_tx_rx_worker_tx(instance, data, SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE); + } else { + //todo checking that he managed to write all the data to the TX buffer + furi_stream_buffer_receive( + instance->stream_tx, &data, size_tx, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); + subghz_tx_rx_worker_tx(instance, data, size_tx); + } + } else { + //recive + if(subghz_tx_rx_worker_rx(instance, data, size_rx)) { + if(furi_stream_buffer_spaces_available(instance->stream_rx) >= size_rx[0]) { + if(instance->callback_have_read && + furi_stream_buffer_bytes_available(instance->stream_rx) == 0) { + callback_rx = true; + } + //todo checking that he managed to write all the data to the RX buffer + furi_stream_buffer_send( + instance->stream_rx, + &data, + size_rx[0], + SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); + if(callback_rx) { + instance->callback_have_read(instance->context_have_read); + callback_rx = false; + } + } else { + //todo RX buffer overflow + } + } + } + + if(timeout_tx) timeout_tx--; + furi_delay_tick(1); + } + + furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_sleep(); + + FURI_LOG_I(TAG, "Worker stop"); + return 0; +} + +SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() { + SubGhzTxRxWorker* instance = malloc(sizeof(SubGhzTxRxWorker)); + + instance->thread = + furi_thread_alloc_ex("SubGhzTxRxWorker", 2048, subghz_tx_rx_worker_thread, instance); + instance->stream_tx = + furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); + instance->stream_rx = + furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); + + instance->status = SubGhzTxRxWorkerStatusIDLE; + instance->worker_stoping = true; + + return instance; +} + +void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance) { + furi_assert(instance); + furi_assert(!instance->worker_running); + furi_stream_buffer_free(instance->stream_tx); + furi_stream_buffer_free(instance->stream_rx); + furi_thread_free(instance->thread); + + free(instance); +} + +bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { + furi_assert(instance); + furi_assert(!instance->worker_running); + bool res = false; + furi_stream_buffer_reset(instance->stream_tx); + furi_stream_buffer_reset(instance->stream_rx); + + instance->worker_running = true; + + if(furi_hal_subghz_is_tx_allowed(frequency)) { + instance->frequency = frequency; + res = true; + } + + furi_thread_start(instance->thread); + + return res; +} + +void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance) { + furi_assert(instance); + furi_assert(instance->worker_running); + + instance->worker_running = false; + + furi_thread_join(instance->thread); +} + +bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance) { + furi_assert(instance); + return instance->worker_running; +} diff --git a/applications/main/subghz/subghz_tx_rx_worker.h b/applications/main/subghz/subghz_tx_rx_worker.h new file mode 100644 index 000000000..ddc02e749 --- /dev/null +++ b/applications/main/subghz/subghz_tx_rx_worker.h @@ -0,0 +1,89 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*SubGhzTxRxWorkerCallbackHaveRead)(void* context); + +typedef struct SubGhzTxRxWorker SubGhzTxRxWorker; + +typedef enum { + SubGhzTxRxWorkerStatusIDLE, + SubGhzTxRxWorkerStatusTx, + SubGhzTxRxWorkerStatusRx, +} SubGhzTxRxWorkerStatus; + +/** + * SubGhzTxRxWorker, add data to transfer + * @param instance Pointer to a SubGhzTxRxWorker instance + * @param data *data + * @param size data size + * @return bool true if ok + */ +bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size); + +/** + * SubGhzTxRxWorker, get available data + * @param instance Pointer to a SubGhzTxRxWorker instance + * @return size_t data size + */ +size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance); + +/** + * SubGhzTxRxWorker, read data + * @param instance Pointer to a SubGhzTxRxWorker instance + * @param data *data + * @param size max data size, which can be read + * @return size_t data size, how much is actually read + */ +size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size); + +/** + * Сallback SubGhzTxRxWorker when there is data to read in an empty buffer + * @param instance Pointer to a SubGhzTxRxWorker instance + * @param callback SubGhzTxRxWorkerCallbackHaveRead callback + * @param context + */ +void subghz_tx_rx_worker_set_callback_have_read( + SubGhzTxRxWorker* instance, + SubGhzTxRxWorkerCallbackHaveRead callback, + void* context); + +/** + * Allocate SubGhzTxRxWorker + * @return SubGhzTxRxWorker* Pointer to a SubGhzTxRxWorker instance + */ +SubGhzTxRxWorker* subghz_tx_rx_worker_alloc(); + +/** + * Free SubGhzTxRxWorker + * @param instance Pointer to a SubGhzTxRxWorker instance + */ +void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance); + +/** + * Start SubGhzTxRxWorker + * @param instance Pointer to a SubGhzTxRxWorker instance + * @return bool - true if ok + */ +bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency); + +/** + * Stop SubGhzTxRxWorker + * @param instance Pointer to a SubGhzTxRxWorker instance + */ +void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance); + +/** + * Check if worker is running + * @param instance Pointer to a SubGhzTxRxWorker instance + * @return bool - true if running + */ +bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/subghz_worker.c b/applications/main/subghz/subghz_worker.c new file mode 100644 index 000000000..50b5aba51 --- /dev/null +++ b/applications/main/subghz/subghz_worker.c @@ -0,0 +1,149 @@ +#include "subghz_worker.h" + +#include + +#define TAG "SubGhzWorker" + +struct SubGhzWorker { + FuriThread* thread; + FuriStreamBuffer* stream; + + volatile bool running; + volatile bool overrun; + + LevelDuration filter_level_duration; + uint16_t filter_duration; + + SubGhzWorkerOverrunCallback overrun_callback; + SubGhzWorkerPairCallback pair_callback; + void* context; +}; + +/** Rx callback timer + * + * @param level received signal level + * @param duration received signal duration + * @param context + */ +void subghz_worker_rx_callback(bool level, uint32_t duration, void* context) { + SubGhzWorker* instance = context; + + LevelDuration level_duration = level_duration_make(level, duration); + if(instance->overrun) { + instance->overrun = false; + level_duration = level_duration_reset(); + } + size_t ret = + furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0); + if(sizeof(LevelDuration) != ret) instance->overrun = true; +} + +/** Worker callback thread + * + * @param context + * @return exit code + */ +static int32_t subghz_worker_thread_callback(void* context) { + SubGhzWorker* instance = context; + + LevelDuration level_duration; + while(instance->running) { + int ret = furi_stream_buffer_receive( + instance->stream, &level_duration, sizeof(LevelDuration), 10); + if(ret == sizeof(LevelDuration)) { + if(level_duration_is_reset(level_duration)) { + FURI_LOG_E(TAG, "Overrun buffer"); + if(instance->overrun_callback) instance->overrun_callback(instance->context); + } else { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + + if((duration < instance->filter_duration) || + (instance->filter_level_duration.level == level)) { + instance->filter_level_duration.duration += duration; + + } else if(instance->filter_level_duration.level != level) { + if(instance->pair_callback) + instance->pair_callback( + instance->context, + instance->filter_level_duration.level, + instance->filter_level_duration.duration); + + instance->filter_level_duration.duration = duration; + instance->filter_level_duration.level = level; + } + } + } + } + + return 0; +} + +SubGhzWorker* subghz_worker_alloc() { + SubGhzWorker* instance = malloc(sizeof(SubGhzWorker)); + + instance->thread = + furi_thread_alloc_ex("SubGhzWorker", 2048, subghz_worker_thread_callback, instance); + + instance->stream = + furi_stream_buffer_alloc(sizeof(LevelDuration) * 4096, sizeof(LevelDuration)); + + //setting default filter in us + instance->filter_duration = 30; + + return instance; +} + +void subghz_worker_free(SubGhzWorker* instance) { + furi_assert(instance); + + furi_stream_buffer_free(instance->stream); + furi_thread_free(instance->thread); + + free(instance); +} + +void subghz_worker_set_overrun_callback( + SubGhzWorker* instance, + SubGhzWorkerOverrunCallback callback) { + furi_assert(instance); + instance->overrun_callback = callback; +} + +void subghz_worker_set_pair_callback(SubGhzWorker* instance, SubGhzWorkerPairCallback callback) { + furi_assert(instance); + instance->pair_callback = callback; +} + +void subghz_worker_set_context(SubGhzWorker* instance, void* context) { + furi_assert(instance); + instance->context = context; +} + +void subghz_worker_start(SubGhzWorker* instance) { + furi_assert(instance); + furi_assert(!instance->running); + + instance->running = true; + + furi_thread_start(instance->thread); +} + +void subghz_worker_stop(SubGhzWorker* instance) { + furi_assert(instance); + furi_assert(instance->running); + + instance->running = false; + + furi_thread_join(instance->thread); +} + +bool subghz_worker_is_running(SubGhzWorker* instance) { + furi_assert(instance); + return instance->running; +} + +void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout) { + furi_assert(instance); + instance->filter_duration = timeout; +} \ No newline at end of file diff --git a/applications/main/subghz/subghz_worker.h b/applications/main/subghz/subghz_worker.h new file mode 100644 index 000000000..657278f50 --- /dev/null +++ b/applications/main/subghz/subghz_worker.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzWorker SubGhzWorker; + +typedef void (*SubGhzWorkerOverrunCallback)(void* context); + +typedef void (*SubGhzWorkerPairCallback)(void* context, bool level, uint32_t duration); + +void subghz_worker_rx_callback(bool level, uint32_t duration, void* context); + +/** + * Allocate SubGhzWorker. + * @return SubGhzWorker* Pointer to a SubGhzWorker instance + */ +SubGhzWorker* subghz_worker_alloc(); + +/** + * Free SubGhzWorker. + * @param instance Pointer to a SubGhzWorker instance + */ +void subghz_worker_free(SubGhzWorker* instance); + +/** + * Overrun callback SubGhzWorker. + * @param instance Pointer to a SubGhzWorker instance + * @param callback SubGhzWorkerOverrunCallback callback + */ +void subghz_worker_set_overrun_callback( + SubGhzWorker* instance, + SubGhzWorkerOverrunCallback callback); + +/** + * Pair callback SubGhzWorker. + * @param instance Pointer to a SubGhzWorker instance + * @param callback SubGhzWorkerOverrunCallback callback + */ +void subghz_worker_set_pair_callback(SubGhzWorker* instance, SubGhzWorkerPairCallback callback); + +/** + * Context callback SubGhzWorker. + * @param instance Pointer to a SubGhzWorker instance + * @param context + */ +void subghz_worker_set_context(SubGhzWorker* instance, void* context); + +/** + * Start SubGhzWorker. + * @param instance Pointer to a SubGhzWorker instance + */ +void subghz_worker_start(SubGhzWorker* instance); + +/** Stop SubGhzWorker + * @param instance Pointer to a SubGhzWorker instance + */ +void subghz_worker_stop(SubGhzWorker* instance); + +/** + * Check if worker is running. + * @param instance Pointer to a SubGhzWorker instance + * @return bool - true if running + */ +bool subghz_worker_is_running(SubGhzWorker* instance); + +/** + * Short duration filter setting. + * glues short durations into 1. The default setting is 30 us, if set to 0 the filter will be disabled + * @param instance Pointer to a SubGhzWorker instance + * @param timeout time in us + */ +void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/transmitter.c b/applications/main/subghz/transmitter.c new file mode 100644 index 000000000..8507ee054 --- /dev/null +++ b/applications/main/subghz/transmitter.c @@ -0,0 +1,64 @@ +#include "transmitter.h" + +#include "protocols/base.h" +#include "registry.h" +#include "protocols/protocol_items.h" + +struct SubGhzTransmitter { + const SubGhzProtocol* protocol; + SubGhzProtocolEncoderBase* protocol_instance; +}; + +SubGhzTransmitter* + subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) { + SubGhzTransmitter* instance = NULL; + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(environment); + + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_name(protocol_registry_items, protocol_name); + + if(protocol && protocol->encoder && protocol->encoder->alloc) { + instance = malloc(sizeof(SubGhzTransmitter)); + instance->protocol = protocol; + instance->protocol_instance = instance->protocol->encoder->alloc(environment); + } + return instance; +} + +void subghz_transmitter_free(SubGhzTransmitter* instance) { + furi_assert(instance); + instance->protocol->encoder->free(instance->protocol_instance); + free(instance); +} + +SubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance) { + furi_assert(instance); + return instance->protocol_instance; +} + +bool subghz_transmitter_stop(SubGhzTransmitter* instance) { + furi_assert(instance); + bool ret = false; + if(instance->protocol && instance->protocol->encoder && instance->protocol->encoder->stop) { + instance->protocol->encoder->stop(instance->protocol_instance); + ret = true; + } + return ret; +} + +bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format) { + furi_assert(instance); + bool ret = false; + if(instance->protocol && instance->protocol->encoder && + instance->protocol->encoder->deserialize) { + ret = + instance->protocol->encoder->deserialize(instance->protocol_instance, flipper_format); + } + return ret; +} + +LevelDuration subghz_transmitter_yield(void* context) { + SubGhzTransmitter* instance = context; + return instance->protocol->encoder->yield(instance->protocol_instance); +} diff --git a/applications/main/subghz/transmitter.h b/applications/main/subghz/transmitter.h new file mode 100644 index 000000000..cce98a463 --- /dev/null +++ b/applications/main/subghz/transmitter.h @@ -0,0 +1,55 @@ +#pragma once + +#include "types.h" +#include "environment.h" +#include "protocols/base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzTransmitter SubGhzTransmitter; + +/** + * Allocate and init SubGhzTransmitter. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzTransmitter* pointer to a SubGhzTransmitter instance + */ +SubGhzTransmitter* + subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name); + +/** + * Free SubGhzTransmitter. + * @param instance Pointer to a SubGhzTransmitter instance + */ +void subghz_transmitter_free(SubGhzTransmitter* instance); + +/** Get protocol instance. + * @param instance Pointer to a SubGhzTransmitter instance + */ +SubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance); + +/** + * Forced transmission stop. + * @param instance Pointer to a SubGhzTransmitter instance + */ +bool subghz_transmitter_stop(SubGhzTransmitter* instance); + +/** + * Deserialize and generating an upload to send. + * @param instance Pointer to a SubGhzTransmitter instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzTransmitter instance + * @return LevelDuration + */ +LevelDuration subghz_transmitter_yield(void* context); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/types.h b/applications/main/subghz/types.h new file mode 100644 index 000000000..9d121dc3c --- /dev/null +++ b/applications/main/subghz/types.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "environment.h" +#include +#include + +#define SUBGHZ_APP_FOLDER ANY_PATH("subghz") +#define SUBGHZ_RAW_FOLDER EXT_PATH("subghz") +#define SUBGHZ_APP_EXTENSION ".sub" + +#define SUBGHZ_KEY_FILE_VERSION 1 +#define SUBGHZ_KEY_FILE_TYPE "Flipper SubGhz Key File" + +#define SUBGHZ_RAW_FILE_VERSION 1 +#define SUBGHZ_RAW_FILE_TYPE "Flipper SubGhz RAW File" + +// Radio Preset +typedef struct { + FuriString* name; + uint32_t frequency; + uint8_t* data; + size_t data_size; +} SubGhzRadioPreset; + +// Allocator and Deallocator +typedef void* (*SubGhzAlloc)(SubGhzEnvironment* environment); +typedef void (*SubGhzFree)(void* context); + +// Serialize and Deserialize +typedef bool ( + *SubGhzSerialize)(void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); +typedef bool (*SubGhzDeserialize)(void* context, FlipperFormat* flipper_format); + +// Decoder specific +typedef void (*SubGhzDecoderFeed)(void* decoder, bool level, uint32_t duration); +typedef void (*SubGhzDecoderReset)(void* decoder); +typedef uint8_t (*SubGhzGetHashData)(void* decoder); +typedef void (*SubGhzGetString)(void* decoder, FuriString* output); + +// Encoder specific +typedef void (*SubGhzEncoderStop)(void* encoder); +typedef LevelDuration (*SubGhzEncoderYield)(void* context); + +typedef struct { + SubGhzAlloc alloc; + SubGhzFree free; + + SubGhzDecoderFeed feed; + SubGhzDecoderReset reset; + + SubGhzGetHashData get_hash_data; + SubGhzGetString get_string; + SubGhzSerialize serialize; + SubGhzDeserialize deserialize; +} SubGhzProtocolDecoder; + +typedef struct { + SubGhzAlloc alloc; + SubGhzFree free; + + SubGhzDeserialize deserialize; + SubGhzEncoderStop stop; + SubGhzEncoderYield yield; +} SubGhzProtocolEncoder; + +typedef enum { + SubGhzProtocolTypeUnknown = 0, + SubGhzProtocolTypeStatic, + SubGhzProtocolTypeDynamic, + SubGhzProtocolTypeRAW, + SubGhzProtocolWeatherStation, + SubGhzProtocolCustom, + SubGhzProtocolTypeBinRAW, +} SubGhzProtocolType; + +typedef enum { + SubGhzProtocolFlag_RAW = (1 << 0), + SubGhzProtocolFlag_Decodable = (1 << 1), + SubGhzProtocolFlag_315 = (1 << 2), + SubGhzProtocolFlag_433 = (1 << 3), + SubGhzProtocolFlag_868 = (1 << 4), + SubGhzProtocolFlag_AM = (1 << 5), + SubGhzProtocolFlag_FM = (1 << 6), + SubGhzProtocolFlag_Save = (1 << 7), + SubGhzProtocolFlag_Load = (1 << 8), + SubGhzProtocolFlag_Send = (1 << 9), + SubGhzProtocolFlag_BinRAW = (1 << 10), +} SubGhzProtocolFlag; + +typedef struct { + const char* name; + SubGhzProtocolType type; + SubGhzProtocolFlag flag; + + const SubGhzProtocolEncoder* encoder; + const SubGhzProtocolDecoder* decoder; +} SubGhzProtocol; diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 0a219a48c..74bdbf5b0 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -14,6 +14,8 @@ #define MENU_ITEMS 4u #define UNLOCK_CNT 3 +#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f + typedef struct { FuriString* item_str; uint8_t type; @@ -34,7 +36,7 @@ static const Icon* ReceiverItemIcons[] = { [SubGhzProtocolTypeUnknown] = &I_Quest_7x8, [SubGhzProtocolTypeStatic] = &I_Static_9x7, [SubGhzProtocolTypeDynamic] = &I_Dynamic_9x7, - [SubGhzProtocolTypeRAW] = &I_Raw_9x7, + [SubGhzProtocolTypeBinRAW] = &I_Raw_9x7, }; typedef enum { @@ -64,6 +66,7 @@ typedef struct { uint16_t history_item; SubGhzViewReceiverBarShow bar_show; SubGhzViewReceiverMode mode; + uint8_t u_rssi; } SubGhzViewReceiverModel; void subghz_view_receiver_set_mode( @@ -73,6 +76,21 @@ void subghz_view_receiver_set_mode( subghz_receiver->view, SubGhzViewReceiverModel * model, { model->mode = mode; }, true); } +void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { + furi_assert(instance); + with_view_model( + instance->view, + SubGhzViewReceiverModel * model, + { + if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { + model->u_rssi = 0; + } else { + model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); + } + }, + true); +} + void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) { furi_assert(subghz_receiver); subghz_receiver->lock_count = 0; @@ -191,6 +209,16 @@ static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool s canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); } +static void subghz_view_rssi_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { + for(uint8_t i = 1; i < model->u_rssi; i++) { + if(i % 5) { + canvas_draw_dot(canvas, 46 + i, 50); + canvas_draw_dot(canvas, 47 + i, 51); + canvas_draw_dot(canvas, 46 + i, 52); + } + } +} + void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); @@ -198,8 +226,9 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { if(model->mode == SubGhzViewReceiverModeLive) { elements_button_left(canvas, "Config"); - canvas_draw_line(canvas, 46, 51, 125, 51); + //canvas_draw_line(canvas, 46, 51, 125, 51); } else { + canvas_draw_line(canvas, 2, 52, 125, 52); canvas_draw_str(canvas, 3, 62, furi_string_get_cstr(model->progress_str)); } @@ -235,7 +264,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Scanning..."); - canvas_draw_line(canvas, 46, 51, 125, 51); + //canvas_draw_line(canvas, 46, 51, 125, 51); canvas_set_font(canvas, FontSecondary); } else { canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52); @@ -245,6 +274,9 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { } } + if(model->mode == SubGhzViewReceiverModeLive) { + subghz_view_rssi_draw(canvas, model); + } switch(model->bar_show) { case SubGhzViewReceiverBarShowLock: canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); @@ -252,7 +284,28 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { break; case SubGhzViewReceiverBarShowToUnlockPress: canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); +#ifdef SUBGHZ_EXT_PRESET_NAME + if(model->history_item == 0 && model->mode == SubGhzViewReceiverModeLive) { + canvas_draw_str( + canvas, + 44 + canvas_string_width(canvas, furi_string_get_cstr(model->frequency_str)) + 1, + 62, + "MHz"); + const char* str = furi_string_get_cstr(model->preset_str); + const uint8_t vertical_offset = 7; + const uint8_t horizontal_offset = 3; + const uint8_t string_width = canvas_string_width(canvas, str); + canvas_draw_str( + canvas, + canvas_width(canvas) - (string_width + horizontal_offset), + vertical_offset, + str); + } else { + canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); + } +#else canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); +#endif canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); canvas_set_font(canvas, FontSecondary); elements_bold_rounded_frame(canvas, 14, 8, 99, 48); diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index 829277174..37eb473de 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -12,6 +12,8 @@ void subghz_view_receiver_set_mode( SubGhzViewReceiver* subghz_receiver, SubGhzViewReceiverMode mode); +void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi); + void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard); void subghz_view_receiver_set_callback( diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index ff971fd3b..9fa304e90 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -23,10 +23,10 @@ static const uint32_t subghz_frequency_list[] = { 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, 309000000, 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, 314350000, 314980000, - 315000000, 318000000, 330000000, 345000000, 348000000, 387000000, 390000000, 418000000, - 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, 434176948, - 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 779000000, 868350000, - 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; + 315000000, 318000000, 330000000, 345000000, 348000000, 350000000, 387000000, 390000000, + 418000000, 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, + 434176948, 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 779000000, + 868350000, 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; typedef enum { SubGhzFrequencyAnalyzerStatusIDLE, @@ -165,6 +165,7 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel // Title canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 7, furi_hal_subghz_get_radio_type() ? "Ext" : "Int"); canvas_draw_str(canvas, 20, 7, "Frequency Analyzer"); // RSSI diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index dcfc281d2..7ba2f4434 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -30,6 +30,7 @@ typedef struct { SubGhzReadRAWStatus status; bool raw_send_only; float raw_threshold_rssi; + bool not_showing_samples; } SubGhzReadRAWModel; void subghz_read_raw_set_callback( @@ -92,7 +93,10 @@ void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample) with_view_model( instance->view, SubGhzReadRAWModel * model, - { furi_string_printf(model->sample_write, "%zu spl.", sample); }, + { + model->not_showing_samples = false; + furi_string_printf(model->sample_write, "%zu spl.", sample); + }, false); } @@ -280,8 +284,15 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { uint8_t graphics_mode = 1; canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 5, 7, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 40, 7, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 0, 7, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 35, 7, furi_string_get_cstr(model->preset_str)); + + if(model->not_showing_samples) { + canvas_draw_str(canvas, 77, 7, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int"); + } else { + canvas_draw_str(canvas, 70, 7, furi_hal_subghz_get_radio_type() ? "E" : "I"); + } + canvas_draw_str_aligned( canvas, 126, 0, AlignRight, AlignTop, furi_string_get_cstr(model->sample_write)); @@ -448,6 +459,7 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { model->status = SubGhzReadRAWStatusStart; model->rssi_history_end = false; model->ind_write = 0; + model->not_showing_samples = true; furi_string_set(model->sample_write, "0 spl."); furi_string_reset(model->file_name); instance->callback(SubGhzCustomEventViewReadRAWErase, instance->context); @@ -509,6 +521,7 @@ void subghz_read_raw_set_status( model->status = SubGhzReadRAWStatusStart; model->rssi_history_end = false; model->ind_write = 0; + model->not_showing_samples = true; furi_string_reset(model->file_name); furi_string_set(model->sample_write, "0 spl."); model->raw_threshold_rssi = raw_threshold_rssi; @@ -530,6 +543,7 @@ void subghz_read_raw_set_status( model->status = SubGhzReadRAWStatusLoadKeyIDLE; model->rssi_history_end = false; model->ind_write = 0; + model->not_showing_samples = true; furi_string_set(model->file_name, file_name); furi_string_set(model->sample_write, "RAW"); }, @@ -542,6 +556,7 @@ void subghz_read_raw_set_status( { model->status = SubGhzReadRAWStatusLoadKeyIDLE; if(!model->ind_write) { + model->not_showing_samples = true; furi_string_set(model->file_name, file_name); furi_string_set(model->sample_write, "RAW"); } else { diff --git a/applications/main/subghz/views/subghz_test_carrier.c b/applications/main/subghz/views/subghz_test_carrier.c index e533a6aac..0cc9a2966 100644 --- a/applications/main/subghz/views/subghz_test_carrier.c +++ b/applications/main/subghz/views/subghz_test_carrier.c @@ -115,14 +115,16 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) { furi_hal_subghz_set_path(model->path); if(model->status == SubGhzTestCarrierModelStatusRx) { - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_rx(); } else { furi_hal_gpio_init( - &gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_cc1101_g0, true); + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, true); if(!furi_hal_subghz_tx()) { - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); subghz_test_carrier->callback( SubGhzTestCarrierEventOnlyRx, subghz_test_carrier->context); } @@ -140,7 +142,7 @@ void subghz_test_carrier_enter(void* context) { furi_hal_subghz_reset(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); with_view_model( subghz_test_carrier->view, diff --git a/applications/main/subghz/views/subghz_test_static.c b/applications/main/subghz/views/subghz_test_static.c index 6abefda76..b9e5a8c9c 100644 --- a/applications/main/subghz/views/subghz_test_static.c +++ b/applications/main/subghz/views/subghz_test_static.c @@ -143,8 +143,9 @@ void subghz_test_static_enter(void* context) { furi_hal_subghz_reset(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_cc1101_g0, false); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); instance->status_tx = SubGhzTestStaticStatusIDLE; with_view_model( diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index dc324a6a5..102639924 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -84,10 +84,15 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - elements_multiline_text(canvas, 0, 7, furi_string_get_cstr(model->key_str)); + elements_multiline_text_aligned( + canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str)); canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str)); canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str)); - if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send"); + + if(model->show_button) { + canvas_draw_str(canvas, 58, 62, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int"); + subghz_view_transmitter_button_right(canvas, "Send"); + } } bool subghz_view_transmitter_input(InputEvent* event, void* context) { diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 24c2d32db..cbe0ec2ed 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,15.0,, +Version,+,15.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -178,6 +178,7 @@ Header,+,lib/subghz/blocks/encoder.h,, Header,+,lib/subghz/blocks/generic.h,, Header,+,lib/subghz/blocks/math.h,, Header,+,lib/subghz/environment.h,, +Header,+,lib/subghz/protocols/protocol_items.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, Header,+,lib/subghz/registry.h,, @@ -534,6 +535,8 @@ Function,-,atoff,float,const char* Function,+,atoi,int,const char* Function,-,atol,long,const char* Function,-,atoll,long long,const char* +Function,-,atomo_decrypt,void,uint8_t* +Function,-,atomo_encrypt,void,uint8_t* Function,-,basename,char*,const char* Function,-,bcmp,int,"const void*, const void*, size_t" Function,-,bcopy,void,"const void*, void*, size_t" @@ -1276,6 +1279,7 @@ Function,+,furi_hal_region_is_frequency_allowed,_Bool,uint32_t Function,+,furi_hal_region_is_provisioned,_Bool, Function,+,furi_hal_region_set,void,FuriHalRegion* Function,-,furi_hal_resources_deinit_early,void, +Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,-,furi_hal_resources_init,void, Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rfid_change_read_config,void,"float, float" @@ -1354,13 +1358,18 @@ Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, Function,+,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* +Function,+,furi_hal_subghz_check_radio,_Bool, +Function,+,furi_hal_subghz_disable_ext_power,void, Function,-,furi_hal_subghz_dump_state,void, +Function,+,furi_hal_subghz_enable_ext_power,void, Function,+,furi_hal_subghz_flush_rx,void, Function,+,furi_hal_subghz_flush_tx,void, Function,+,furi_hal_subghz_get_lqi,uint8_t, +Function,+,furi_hal_subghz_get_radio_type,SubGhzRadioType, Function,+,furi_hal_subghz_get_rssi,float, Function,+,furi_hal_subghz_idle,void, Function,-,furi_hal_subghz_init,void, +Function,+,furi_hal_subghz_init_check,_Bool, Function,+,furi_hal_subghz_is_async_tx_complete,_Bool, Function,+,furi_hal_subghz_is_frequency_valid,_Bool,uint32_t Function,+,furi_hal_subghz_is_rx_data_crc_valid,_Bool, @@ -1377,6 +1386,7 @@ Function,+,furi_hal_subghz_set_async_mirror_pin,void,const GpioPin* Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath +Function,+,furi_hal_subghz_set_radio_type,_Bool,SubGhzRadioType Function,-,furi_hal_subghz_shutdown,void, Function,+,furi_hal_subghz_sleep,void, Function,+,furi_hal_subghz_start_async_rx,void,"FuriHalSubGhzCaptureCallback, void*" @@ -1746,6 +1756,8 @@ Function,-,j1f,float,float Function,-,jn,double,"int, double" Function,-,jnf,float,"int, float" Function,-,jrand48,long,unsigned short[3] +Function,-,keeloq_reset_kl_type,void, +Function,-,keeloq_reset_mfname,void, Function,-,l64a,char*,long Function,-,labs,long,long Function,-,lcong48,void,unsigned short[7] @@ -2505,6 +2517,8 @@ Function,+,srand,void,unsigned Function,-,srand48,void,long Function,-,srandom,void,unsigned Function,+,sscanf,int,"const char*, const char*, ..." +Function,-,star_line_reset_kl_type,void, +Function,-,star_line_reset_mfname,void, Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*" Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*" Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*" @@ -2686,28 +2700,541 @@ Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" -Function,+,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" +Function,-,subghz_protocol_decoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_alutech_at_4n_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_alutech_at_4n_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_alutech_at_4n_free,void,void* +Function,-,subghz_protocol_decoder_alutech_at_4n_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_alutech_at_4n_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_alutech_at_4n_reset,void,void* +Function,-,subghz_protocol_decoder_alutech_at_4n_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_ansonic_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_ansonic_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_ansonic_free,void,void* +Function,-,subghz_protocol_decoder_ansonic_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_ansonic_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_ansonic_reset,void,void* +Function,-,subghz_protocol_decoder_ansonic_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" +Function,-,subghz_protocol_decoder_bett_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_bett_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_bett_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_bett_free,void,void* +Function,-,subghz_protocol_decoder_bett_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_bett_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_bett_reset,void,void* +Function,-,subghz_protocol_decoder_bett_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_bin_raw_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_bin_raw_data_input_rssi,void,"SubGhzProtocolDecoderBinRAW*, float" +Function,-,subghz_protocol_decoder_bin_raw_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_bin_raw_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_bin_raw_free,void,void* +Function,-,subghz_protocol_decoder_bin_raw_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_bin_raw_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_bin_raw_reset,void,void* +Function,-,subghz_protocol_decoder_bin_raw_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_came_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_came_atomo_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_came_atomo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_came_atomo_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_came_atomo_free,void,void* +Function,-,subghz_protocol_decoder_came_atomo_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_came_atomo_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_came_atomo_reset,void,void* +Function,-,subghz_protocol_decoder_came_atomo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_came_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_came_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_came_free,void,void* +Function,-,subghz_protocol_decoder_came_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_came_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_came_reset,void,void* +Function,-,subghz_protocol_decoder_came_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_came_twee_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_came_twee_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_came_twee_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_came_twee_free,void,void* +Function,-,subghz_protocol_decoder_came_twee_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_came_twee_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_came_twee_reset,void,void* +Function,-,subghz_protocol_decoder_came_twee_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_chamb_code_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_chamb_code_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_chamb_code_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_chamb_code_free,void,void* +Function,-,subghz_protocol_decoder_chamb_code_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_chamb_code_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_chamb_code_reset,void,void* +Function,-,subghz_protocol_decoder_chamb_code_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_clemsa_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_clemsa_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_clemsa_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_clemsa_free,void,void* +Function,-,subghz_protocol_decoder_clemsa_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_clemsa_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_clemsa_reset,void,void* +Function,-,subghz_protocol_decoder_clemsa_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_doitrand_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_doitrand_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_doitrand_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_doitrand_free,void,void* +Function,-,subghz_protocol_decoder_doitrand_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_doitrand_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_doitrand_reset,void,void* +Function,-,subghz_protocol_decoder_doitrand_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_dooya_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_dooya_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_dooya_free,void,void* +Function,-,subghz_protocol_decoder_dooya_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_dooya_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_dooya_reset,void,void* +Function,-,subghz_protocol_decoder_dooya_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_faac_slh_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_faac_slh_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_faac_slh_free,void,void* +Function,-,subghz_protocol_decoder_faac_slh_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_faac_slh_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_faac_slh_reset,void,void* +Function,-,subghz_protocol_decoder_faac_slh_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_gate_tx_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_gate_tx_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_gate_tx_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_gate_tx_free,void,void* +Function,-,subghz_protocol_decoder_gate_tx_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_gate_tx_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_gate_tx_reset,void,void* +Function,-,subghz_protocol_decoder_gate_tx_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_holtek_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_holtek_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_holtek_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_holtek_free,void,void* +Function,-,subghz_protocol_decoder_holtek_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_holtek_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_holtek_reset,void,void* +Function,-,subghz_protocol_decoder_holtek_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_holtek_th12x_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_holtek_th12x_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_holtek_th12x_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_holtek_th12x_free,void,void* +Function,-,subghz_protocol_decoder_holtek_th12x_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_holtek_th12x_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_holtek_th12x_reset,void,void* +Function,-,subghz_protocol_decoder_holtek_th12x_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_honeywell_wdb_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_honeywell_wdb_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_honeywell_wdb_free,void,void* +Function,-,subghz_protocol_decoder_honeywell_wdb_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_honeywell_wdb_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_honeywell_wdb_reset,void,void* +Function,-,subghz_protocol_decoder_honeywell_wdb_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_hormann_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_hormann_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_hormann_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_hormann_free,void,void* +Function,-,subghz_protocol_decoder_hormann_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_hormann_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_hormann_reset,void,void* +Function,-,subghz_protocol_decoder_hormann_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_ido_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_ido_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_ido_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_ido_free,void,void* +Function,-,subghz_protocol_decoder_ido_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_ido_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_ido_reset,void,void* +Function,-,subghz_protocol_decoder_ido_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_intertechno_v3_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_intertechno_v3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_intertechno_v3_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_intertechno_v3_free,void,void* +Function,-,subghz_protocol_decoder_intertechno_v3_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_intertechno_v3_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_intertechno_v3_reset,void,void* +Function,-,subghz_protocol_decoder_intertechno_v3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_keeloq_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_keeloq_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_keeloq_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_keeloq_free,void,void* +Function,-,subghz_protocol_decoder_keeloq_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_keeloq_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_keeloq_reset,void,void* +Function,-,subghz_protocol_decoder_keeloq_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_kia_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_kia_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_kia_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_kia_free,void,void* +Function,-,subghz_protocol_decoder_kia_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_kia_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_kia_reset,void,void* +Function,-,subghz_protocol_decoder_kia_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_free,void,void* +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_reset,void,void* +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_linear_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_linear_delta3_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_linear_delta3_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_linear_delta3_free,void,void* +Function,-,subghz_protocol_decoder_linear_delta3_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_linear_delta3_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_linear_delta3_reset,void,void* +Function,-,subghz_protocol_decoder_linear_delta3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_linear_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_linear_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_linear_free,void,void* +Function,-,subghz_protocol_decoder_linear_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_linear_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_linear_reset,void,void* +Function,-,subghz_protocol_decoder_linear_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_magellan_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_magellan_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_magellan_free,void,void* +Function,-,subghz_protocol_decoder_magellan_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_magellan_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_magellan_reset,void,void* +Function,-,subghz_protocol_decoder_magellan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_marantec_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_marantec_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_marantec_free,void,void* +Function,-,subghz_protocol_decoder_marantec_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_marantec_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_marantec_reset,void,void* +Function,-,subghz_protocol_decoder_marantec_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_megacode_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_megacode_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_megacode_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_megacode_free,void,void* +Function,-,subghz_protocol_decoder_megacode_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_megacode_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_megacode_reset,void,void* +Function,-,subghz_protocol_decoder_megacode_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nero_radio_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_nero_radio_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nero_radio_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_nero_radio_free,void,void* +Function,-,subghz_protocol_decoder_nero_radio_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_nero_radio_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_nero_radio_reset,void,void* +Function,-,subghz_protocol_decoder_nero_radio_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nero_sketch_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_nero_sketch_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nero_sketch_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_nero_sketch_free,void,void* +Function,-,subghz_protocol_decoder_nero_sketch_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_nero_sketch_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_nero_sketch_reset,void,void* +Function,-,subghz_protocol_decoder_nero_sketch_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nice_flo_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_nice_flo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nice_flo_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_nice_flo_free,void,void* +Function,-,subghz_protocol_decoder_nice_flo_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_nice_flo_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_nice_flo_reset,void,void* +Function,-,subghz_protocol_decoder_nice_flo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nice_flor_s_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_nice_flor_s_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nice_flor_s_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_nice_flor_s_free,void,void* +Function,-,subghz_protocol_decoder_nice_flor_s_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_nice_flor_s_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_nice_flor_s_reset,void,void* +Function,-,subghz_protocol_decoder_nice_flor_s_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_phoenix_v2_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_phoenix_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_phoenix_v2_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_phoenix_v2_free,void,void* +Function,-,subghz_protocol_decoder_phoenix_v2_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_phoenix_v2_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_phoenix_v2_reset,void,void* +Function,-,subghz_protocol_decoder_phoenix_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_power_smart_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_power_smart_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_power_smart_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_power_smart_free,void,void* +Function,-,subghz_protocol_decoder_power_smart_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_power_smart_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_power_smart_reset,void,void* +Function,-,subghz_protocol_decoder_power_smart_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_protocol_decoder_princeton_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_princeton_feed,void,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_princeton_free,void,void* +Function,+,subghz_protocol_decoder_princeton_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_princeton_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_princeton_reset,void,void* +Function,+,subghz_protocol_decoder_princeton_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_decoder_raw_deserialize,_Bool,"void*, FlipperFormat*" Function,+,subghz_protocol_decoder_raw_feed,void,"void*, _Bool, uint32_t" Function,+,subghz_protocol_decoder_raw_free,void,void* -Function,+,subghz_protocol_decoder_raw_get_hash_data,uint8_t,void* Function,+,subghz_protocol_decoder_raw_get_string,void,"void*, FuriString*" Function,+,subghz_protocol_decoder_raw_reset,void,void* -Function,+,subghz_protocol_decoder_raw_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,+,subghz_protocol_decoder_raw_set_auto_mode,void,"void*, _Bool" -Function,+,subghz_protocol_decoder_raw_set_rssi_threshold,void,"void*, int" -Function,+,subghz_protocol_decoder_raw_write_data,_Bool,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_encoder_get_rssi_threshold,int,void* +Function,-,subghz_protocol_decoder_scher_khan_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_scher_khan_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_scher_khan_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_scher_khan_free,void,void* +Function,-,subghz_protocol_decoder_scher_khan_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_scher_khan_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_scher_khan_reset,void,void* +Function,-,subghz_protocol_decoder_scher_khan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_secplus_v1_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_secplus_v1_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_secplus_v1_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_secplus_v1_free,void,void* +Function,-,subghz_protocol_decoder_secplus_v1_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_secplus_v1_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_secplus_v1_reset,void,void* +Function,-,subghz_protocol_decoder_secplus_v1_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_secplus_v2_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_secplus_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_secplus_v2_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_secplus_v2_free,void,void* +Function,-,subghz_protocol_decoder_secplus_v2_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_secplus_v2_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_secplus_v2_reset,void,void* +Function,-,subghz_protocol_decoder_secplus_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_smc5326_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_smc5326_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_smc5326_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_smc5326_free,void,void* +Function,-,subghz_protocol_decoder_smc5326_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_smc5326_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_smc5326_reset,void,void* +Function,-,subghz_protocol_decoder_smc5326_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_somfy_keytis_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_somfy_keytis_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_somfy_keytis_free,void,void* +Function,-,subghz_protocol_decoder_somfy_keytis_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_somfy_keytis_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_somfy_keytis_reset,void,void* +Function,-,subghz_protocol_decoder_somfy_keytis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_somfy_telis_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_somfy_telis_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_somfy_telis_free,void,void* +Function,-,subghz_protocol_decoder_somfy_telis_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_somfy_telis_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_somfy_telis_reset,void,void* +Function,-,subghz_protocol_decoder_somfy_telis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_star_line_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_star_line_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_star_line_free,void,void* +Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_star_line_reset,void,void* +Function,-,subghz_protocol_decoder_star_line_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_ansonic_free,void,void* +Function,-,subghz_protocol_encoder_ansonic_stop,void,void* +Function,-,subghz_protocol_encoder_ansonic_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_bett_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_bett_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_bett_free,void,void* +Function,-,subghz_protocol_encoder_bett_stop,void,void* +Function,-,subghz_protocol_encoder_bett_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_bin_raw_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_bin_raw_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_bin_raw_free,void,void* +Function,-,subghz_protocol_encoder_bin_raw_stop,void,void* +Function,-,subghz_protocol_encoder_bin_raw_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_came_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_came_atomo_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_came_atomo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_came_atomo_free,void,void* +Function,-,subghz_protocol_encoder_came_atomo_stop,void,void* +Function,-,subghz_protocol_encoder_came_atomo_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_came_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_came_free,void,void* +Function,-,subghz_protocol_encoder_came_stop,void,void* +Function,-,subghz_protocol_encoder_came_twee_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_came_twee_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_came_twee_free,void,void* +Function,-,subghz_protocol_encoder_came_twee_stop,void,void* +Function,-,subghz_protocol_encoder_came_twee_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_came_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_chamb_code_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_chamb_code_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_chamb_code_free,void,void* +Function,-,subghz_protocol_encoder_chamb_code_stop,void,void* +Function,-,subghz_protocol_encoder_chamb_code_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_clemsa_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_clemsa_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_clemsa_free,void,void* +Function,-,subghz_protocol_encoder_clemsa_stop,void,void* +Function,-,subghz_protocol_encoder_clemsa_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_doitrand_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_doitrand_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_doitrand_free,void,void* +Function,-,subghz_protocol_encoder_doitrand_stop,void,void* +Function,-,subghz_protocol_encoder_doitrand_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_dooya_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_dooya_free,void,void* +Function,-,subghz_protocol_encoder_dooya_stop,void,void* +Function,-,subghz_protocol_encoder_dooya_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_faac_slh_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_faac_slh_free,void,void* +Function,-,subghz_protocol_encoder_faac_slh_stop,void,void* +Function,-,subghz_protocol_encoder_faac_slh_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_gate_tx_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_gate_tx_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_gate_tx_free,void,void* +Function,-,subghz_protocol_encoder_gate_tx_stop,void,void* +Function,-,subghz_protocol_encoder_gate_tx_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_holtek_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_holtek_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_holtek_free,void,void* +Function,-,subghz_protocol_encoder_holtek_stop,void,void* +Function,-,subghz_protocol_encoder_holtek_th12x_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_holtek_th12x_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_holtek_th12x_free,void,void* +Function,-,subghz_protocol_encoder_holtek_th12x_stop,void,void* +Function,-,subghz_protocol_encoder_holtek_th12x_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_holtek_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_honeywell_wdb_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_honeywell_wdb_free,void,void* +Function,-,subghz_protocol_encoder_honeywell_wdb_stop,void,void* +Function,-,subghz_protocol_encoder_honeywell_wdb_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_hormann_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_hormann_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_hormann_free,void,void* +Function,-,subghz_protocol_encoder_hormann_stop,void,void* +Function,-,subghz_protocol_encoder_hormann_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_intertechno_v3_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_intertechno_v3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_intertechno_v3_free,void,void* +Function,-,subghz_protocol_encoder_intertechno_v3_stop,void,void* +Function,-,subghz_protocol_encoder_intertechno_v3_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_keeloq_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_keeloq_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_keeloq_free,void,void* +Function,-,subghz_protocol_encoder_keeloq_stop,void,void* +Function,-,subghz_protocol_encoder_keeloq_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_kinggates_stylo_4k_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_kinggates_stylo_4k_free,void,void* +Function,-,subghz_protocol_encoder_kinggates_stylo_4k_stop,void,void* +Function,-,subghz_protocol_encoder_kinggates_stylo_4k_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_linear_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_linear_delta3_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_linear_delta3_free,void,void* +Function,-,subghz_protocol_encoder_linear_delta3_stop,void,void* +Function,-,subghz_protocol_encoder_linear_delta3_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_linear_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_linear_free,void,void* +Function,-,subghz_protocol_encoder_linear_stop,void,void* +Function,-,subghz_protocol_encoder_linear_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_magellan_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_magellan_free,void,void* +Function,-,subghz_protocol_encoder_magellan_stop,void,void* +Function,-,subghz_protocol_encoder_magellan_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_marantec_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_marantec_free,void,void* +Function,-,subghz_protocol_encoder_marantec_stop,void,void* +Function,-,subghz_protocol_encoder_marantec_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_megacode_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_megacode_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_megacode_free,void,void* +Function,-,subghz_protocol_encoder_megacode_stop,void,void* +Function,-,subghz_protocol_encoder_megacode_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_nero_radio_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_nero_radio_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nero_radio_free,void,void* +Function,-,subghz_protocol_encoder_nero_radio_stop,void,void* +Function,-,subghz_protocol_encoder_nero_radio_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_nero_sketch_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_nero_sketch_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nero_sketch_free,void,void* +Function,-,subghz_protocol_encoder_nero_sketch_stop,void,void* +Function,-,subghz_protocol_encoder_nero_sketch_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_nice_flo_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_nice_flo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nice_flo_free,void,void* +Function,-,subghz_protocol_encoder_nice_flo_stop,void,void* +Function,-,subghz_protocol_encoder_nice_flo_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_nice_flor_s_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_nice_flor_s_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nice_flor_s_free,void,void* +Function,-,subghz_protocol_encoder_nice_flor_s_stop,void,void* +Function,-,subghz_protocol_encoder_nice_flor_s_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_phoenix_v2_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_phoenix_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_phoenix_v2_free,void,void* +Function,-,subghz_protocol_encoder_phoenix_v2_stop,void,void* +Function,-,subghz_protocol_encoder_phoenix_v2_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_power_smart_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_power_smart_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_power_smart_free,void,void* +Function,-,subghz_protocol_encoder_power_smart_stop,void,void* +Function,-,subghz_protocol_encoder_power_smart_yield,LevelDuration,void* +Function,+,subghz_protocol_encoder_princeton_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_princeton_free,void,void* +Function,+,subghz_protocol_encoder_princeton_stop,void,void* +Function,+,subghz_protocol_encoder_princeton_yield,LevelDuration,void* Function,+,subghz_protocol_encoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_encoder_raw_deserialize,_Bool,"void*, FlipperFormat*" Function,+,subghz_protocol_encoder_raw_free,void,void* Function,+,subghz_protocol_encoder_raw_stop,void,void* Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_secplus_v1_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_secplus_v1_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_secplus_v1_free,void,void* +Function,-,subghz_protocol_encoder_secplus_v1_stop,void,void* +Function,-,subghz_protocol_encoder_secplus_v1_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_secplus_v2_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_secplus_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_secplus_v2_free,void,void* +Function,-,subghz_protocol_encoder_secplus_v2_stop,void,void* +Function,-,subghz_protocol_encoder_secplus_v2_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_smc5326_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_smc5326_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_smc5326_free,void,void* +Function,-,subghz_protocol_encoder_smc5326_stop,void,void* +Function,-,subghz_protocol_encoder_smc5326_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_somfy_keytis_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_somfy_keytis_free,void,void* +Function,-,subghz_protocol_encoder_somfy_keytis_stop,void,void* +Function,-,subghz_protocol_encoder_somfy_keytis_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_somfy_telis_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_somfy_telis_free,void,void* +Function,-,subghz_protocol_encoder_somfy_telis_stop,void,void* +Function,-,subghz_protocol_encoder_somfy_telis_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_star_line_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_star_line_free,void,void* +Function,-,subghz_protocol_encoder_star_line_stop,void,void* +Function,-,subghz_protocol_encoder_star_line_yield,LevelDuration,void* +Function,-,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*" +Function,-,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*" +Function,-,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" +Function,-,subghz_protocol_nice_flor_s_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" +Function,-,subghz_protocol_nice_flor_s_encrypt,uint64_t,"uint64_t, const char*" Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* @@ -2717,10 +3244,14 @@ Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry* Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t" Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" +Function,-,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t +Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" +Function,-,subghz_protocol_somfy_keytis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" +Function,-,subghz_protocol_somfy_telis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" +Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" Function,+,subghz_receiver_free,void,SubGhzReceiver* -Function,+,subghz_receiver_get_filter,SubGhzProtocolFlag,SubGhzReceiver* Function,+,subghz_receiver_reset,void,SubGhzReceiver* Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase*,"SubGhzReceiver*, const char*" Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag" @@ -4178,12 +4709,15 @@ Variable,+,furi_hal_spi_bus_handle_nfc,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_sd_fast,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_sd_slow,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_subghz,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_subghz_ext,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_subghz_int,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_r,FuriHalSpiBus, Variable,+,furi_hal_spi_preset_1edge_low_16m,const LL_SPI_InitTypeDef, Variable,+,furi_hal_spi_preset_1edge_low_2m,const LL_SPI_InitTypeDef, Variable,+,furi_hal_spi_preset_1edge_low_4m,const LL_SPI_InitTypeDef, Variable,+,furi_hal_spi_preset_1edge_low_8m,const LL_SPI_InitTypeDef, Variable,+,furi_hal_spi_preset_2edge_low_8m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_subghz,volatile FuriHalSubGhz, Variable,+,gpio_button_back,const GpioPin, Variable,+,gpio_button_down,const GpioPin, Variable,+,gpio_button_left,const GpioPin, @@ -4191,6 +4725,7 @@ Variable,+,gpio_button_ok,const GpioPin, Variable,+,gpio_button_right,const GpioPin, Variable,+,gpio_button_up,const GpioPin, Variable,+,gpio_cc1101_g0,const GpioPin, +Variable,+,gpio_cc1101_g0_ext,const GpioPin, Variable,+,gpio_display_cs,const GpioPin, Variable,+,gpio_display_di,const GpioPin, Variable,+,gpio_display_rst_n,const GpioPin, @@ -4221,9 +4756,13 @@ Variable,+,gpio_spi_d_miso,const GpioPin, Variable,+,gpio_spi_d_mosi,const GpioPin, Variable,+,gpio_spi_d_sck,const GpioPin, Variable,+,gpio_spi_r_miso,const GpioPin, +Variable,+,gpio_spi_r_miso_ext,const GpioPin, Variable,+,gpio_spi_r_mosi,const GpioPin, +Variable,+,gpio_spi_r_mosi_ext,const GpioPin, Variable,+,gpio_spi_r_sck,const GpioPin, +Variable,+,gpio_spi_r_sck_ext,const GpioPin, Variable,+,gpio_subghz_cs,const GpioPin, +Variable,+,gpio_subghz_cs_ext,const GpioPin, Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, @@ -4426,9 +4965,133 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, +Variable,-,subghz_protocol_alutech_at_4n,const SubGhzProtocol, +Variable,-,subghz_protocol_alutech_at_4n_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_alutech_at_4n_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_ansonic,const SubGhzProtocol, +Variable,-,subghz_protocol_ansonic_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_ansonic_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_bett,const SubGhzProtocol, +Variable,-,subghz_protocol_bett_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_bett_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_bin_raw,const SubGhzProtocol, +Variable,-,subghz_protocol_bin_raw_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_bin_raw_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_came,const SubGhzProtocol, +Variable,-,subghz_protocol_came_atomo,const SubGhzProtocol, +Variable,-,subghz_protocol_came_atomo_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_came_atomo_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_came_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_came_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_came_twee,const SubGhzProtocol, +Variable,-,subghz_protocol_came_twee_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_came_twee_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_chamb_code,const SubGhzProtocol, +Variable,-,subghz_protocol_chamb_code_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_chamb_code_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_clemsa,const SubGhzProtocol, +Variable,-,subghz_protocol_clemsa_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_clemsa_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_doitrand,const SubGhzProtocol, +Variable,-,subghz_protocol_doitrand_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_doitrand_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_dooya,const SubGhzProtocol, +Variable,-,subghz_protocol_dooya_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_dooya_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_faac_slh,const SubGhzProtocol, +Variable,-,subghz_protocol_faac_slh_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_faac_slh_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_gate_tx,const SubGhzProtocol, +Variable,-,subghz_protocol_gate_tx_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_gate_tx_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_holtek,const SubGhzProtocol, +Variable,-,subghz_protocol_holtek_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_holtek_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_holtek_th12x,const SubGhzProtocol, +Variable,-,subghz_protocol_holtek_th12x_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_holtek_th12x_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_honeywell_wdb,const SubGhzProtocol, +Variable,-,subghz_protocol_honeywell_wdb_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_honeywell_wdb_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_hormann,const SubGhzProtocol, +Variable,-,subghz_protocol_hormann_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_hormann_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_ido,const SubGhzProtocol, +Variable,-,subghz_protocol_ido_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_ido_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_intertechno_v3,const SubGhzProtocol, +Variable,-,subghz_protocol_intertechno_v3_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_intertechno_v3_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_keeloq,const SubGhzProtocol, +Variable,-,subghz_protocol_keeloq_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_keeloq_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_kia,const SubGhzProtocol, +Variable,-,subghz_protocol_kia_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_kia_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_kinggates_stylo_4k,const SubGhzProtocol, +Variable,-,subghz_protocol_kinggates_stylo_4k_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_kinggates_stylo_4k_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_linear,const SubGhzProtocol, +Variable,-,subghz_protocol_linear_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_linear_delta3,const SubGhzProtocol, +Variable,-,subghz_protocol_linear_delta3_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_linear_delta3_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_linear_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_magellan,const SubGhzProtocol, +Variable,-,subghz_protocol_magellan_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_magellan_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_marantec,const SubGhzProtocol, +Variable,-,subghz_protocol_marantec_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_marantec_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_megacode,const SubGhzProtocol, +Variable,-,subghz_protocol_megacode_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_megacode_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_nero_radio,const SubGhzProtocol, +Variable,-,subghz_protocol_nero_radio_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_nero_radio_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_nero_sketch,const SubGhzProtocol, +Variable,-,subghz_protocol_nero_sketch_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_nero_sketch_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_nice_flo,const SubGhzProtocol, +Variable,-,subghz_protocol_nice_flo_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_nice_flo_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_nice_flor_s,const SubGhzProtocol, +Variable,-,subghz_protocol_nice_flor_s_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_nice_flor_s_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_phoenix_v2,const SubGhzProtocol, +Variable,-,subghz_protocol_phoenix_v2_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_phoenix_v2_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_power_smart,const SubGhzProtocol, +Variable,-,subghz_protocol_power_smart_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_power_smart_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_princeton,const SubGhzProtocol, +Variable,-,subghz_protocol_princeton_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_princeton_encoder,const SubGhzProtocolEncoder, Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_registry,const SubGhzProtocolRegistry, +Variable,-,subghz_protocol_scher_khan,const SubGhzProtocol, +Variable,-,subghz_protocol_scher_khan_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_scher_khan_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_secplus_v1,const SubGhzProtocol, +Variable,-,subghz_protocol_secplus_v1_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_secplus_v1_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_secplus_v2,const SubGhzProtocol, +Variable,-,subghz_protocol_secplus_v2_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_secplus_v2_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_smc5326,const SubGhzProtocol, +Variable,-,subghz_protocol_smc5326_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_smc5326_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_somfy_keytis,const SubGhzProtocol, +Variable,-,subghz_protocol_somfy_keytis_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_somfy_keytis_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_somfy_telis,const SubGhzProtocol, +Variable,-,subghz_protocol_somfy_telis_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_somfy_telis_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_star_line,const SubGhzProtocol, +Variable,-,subghz_protocol_star_line_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_star_line_encoder,const SubGhzProtocolEncoder, Variable,-,suboptarg,char*, Variable,-,u8g2_cb_mirror,const u8g2_cb_t, Variable,-,u8g2_cb_r0,const u8g2_cb_t, diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 03cc6e714..4e63263cf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -8,9 +8,11 @@ const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; const GpioPin gpio_cc1101_g0 = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin}; +const GpioPin gpio_cc1101_g0_ext = {.port = GPIOB, .pin = LL_GPIO_PIN_2}; const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin}; const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin}; +const GpioPin gpio_subghz_cs_ext = {.port = GPIOA, .pin = LL_GPIO_PIN_4}; const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin}; const GpioPin gpio_display_rst_n = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin}; const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin}; @@ -31,6 +33,9 @@ const GpioPin gpio_spi_d_sck = {.port = SPI_D_SCK_GPIO_Port, .pin = SPI_D_SCK_Pi const GpioPin gpio_spi_r_miso = {.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin}; const GpioPin gpio_spi_r_mosi = {.port = SPI_R_MOSI_GPIO_Port, .pin = SPI_R_MOSI_Pin}; const GpioPin gpio_spi_r_sck = {.port = SPI_R_SCK_GPIO_Port, .pin = SPI_R_SCK_Pin}; +const GpioPin gpio_spi_r_miso_ext = {.port = GPIOA, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_spi_r_mosi_ext = {.port = GPIOA, .pin = LL_GPIO_PIN_7}; +const GpioPin gpio_spi_r_sck_ext = {.port = GPIOB, .pin = LL_GPIO_PIN_3}; const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0}; const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1}; @@ -191,3 +196,26 @@ void furi_hal_resources_init() { NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI15_10_IRQn); } + +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { + if(gpio == &gpio_ext_pa7) + return 2; + else if(gpio == &gpio_ext_pa6) + return 3; + else if(gpio == &gpio_ext_pa4) + return 4; + else if(gpio == &gpio_ext_pb3) + return 5; + else if(gpio == &gpio_ext_pb2) + return 6; + else if(gpio == &gpio_ext_pc3) + return 7; + else if(gpio == &gpio_ext_pc1) + return 15; + else if(gpio == &gpio_ext_pc0) + return 16; + else if(gpio == &ibutton_gpio) + return 17; + else + return -1; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index 51ea7bcc1..33e2a3289 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -54,9 +54,11 @@ extern const GpioPin vibro_gpio; extern const GpioPin ibutton_gpio; extern const GpioPin gpio_cc1101_g0; +extern const GpioPin gpio_cc1101_g0_ext; extern const GpioPin gpio_rf_sw_0; extern const GpioPin gpio_subghz_cs; +extern const GpioPin gpio_subghz_cs_ext; extern const GpioPin gpio_display_cs; extern const GpioPin gpio_display_rst_n; extern const GpioPin gpio_display_di; @@ -77,6 +79,9 @@ extern const GpioPin gpio_spi_d_sck; extern const GpioPin gpio_spi_r_miso; extern const GpioPin gpio_spi_r_mosi; extern const GpioPin gpio_spi_r_sck; +extern const GpioPin gpio_spi_r_miso_ext; +extern const GpioPin gpio_spi_r_mosi_ext; +extern const GpioPin gpio_spi_r_sck_ext; extern const GpioPin gpio_ext_pc0; extern const GpioPin gpio_ext_pc1; @@ -216,6 +221,13 @@ void furi_hal_resources_deinit_early(); void furi_hal_resources_init(); +/** + * Get a corresponding external connector pin number for a gpio + * @param gpio GpioPin + * @return pin number or -1 if gpio is not on the external connector + */ +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c index 9cf332dac..695418af1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c @@ -2,6 +2,7 @@ #include #include #include +#include #define TAG "FuriHalSpiConfig" @@ -89,7 +90,7 @@ void furi_hal_spi_config_deinit_early() { void furi_hal_spi_config_init() { furi_hal_spi_bus_init(&furi_hal_spi_bus_r); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_bus_handle_init(furi_hal_subghz.spi_bus_handle); furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); @@ -285,6 +286,15 @@ static void furi_hal_spi_bus_handle_subghz_event_callback( furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_8m); } +FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz_int = { + .bus = &furi_hal_spi_bus_r, + .callback = furi_hal_spi_bus_handle_subghz_event_callback, + .miso = &gpio_spi_r_miso, + .mosi = &gpio_spi_r_mosi, + .sck = &gpio_spi_r_sck, + .cs = &gpio_subghz_cs, +}; + FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz = { .bus = &furi_hal_spi_bus_r, .callback = furi_hal_spi_bus_handle_subghz_event_callback, @@ -294,6 +304,15 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz = { .cs = &gpio_subghz_cs, }; +FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz_ext = { + .bus = &furi_hal_spi_bus_r, + .callback = furi_hal_spi_bus_handle_subghz_event_callback, + .miso = &gpio_ext_pa6, + .mosi = &gpio_ext_pa7, + .sck = &gpio_ext_pb3, + .cs = &gpio_ext_pa4, +}; + static void furi_hal_spi_bus_handle_nfc_event_callback( FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.h b/firmware/targets/f7/furi_hal/furi_hal_spi_config.h index eab633a19..8ea138bdc 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_config.h +++ b/firmware/targets/f7/furi_hal/furi_hal_spi_config.h @@ -27,8 +27,12 @@ extern FuriHalSpiBus furi_hal_spi_bus_r; /** Furi Hal Spi Bus D (Display, SdCard) */ extern FuriHalSpiBus furi_hal_spi_bus_d; -/** CC1101 on `furi_hal_spi_bus_r` */ +/** CC1101 on current SPI bus */ extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz; +/** CC1101 on `furi_hal_spi_bus_r` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz_int; +/** CC1101 on external `furi_hal_spi_bus_r` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz_ext; /** ST25R3916 on `furi_hal_spi_bus_r` */ extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc; diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index ccea7728d..c2c238a13 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -1,111 +1,204 @@ #include #include -#include #include #include #include #include #include - -#include +#include #include +#include + #include #include #include #define TAG "FuriHalSubGhz" +//Initialisation timeout (ms) +#define INIT_TIMEOUT 10 static uint32_t furi_hal_subghz_debug_gpio_buff[2]; +static bool last_OTG_state = false; -typedef struct { - volatile SubGhzState state; - volatile SubGhzRegulation regulation; - volatile FuriHalSubGhzPreset preset; - const GpioPin* async_mirror_pin; -} FuriHalSubGhz; +/* DMA Channels definition */ +#define SUBGHZ_DMA DMA2 +#define SUBGHZ_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define SUBGHZ_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define SUBGHZ_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL +#define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL volatile FuriHalSubGhz furi_hal_subghz = { .state = SubGhzStateInit, .regulation = SubGhzRegulationTxRx, .preset = FuriHalSubGhzPresetIDLE, .async_mirror_pin = NULL, + .radio_type = SubGhzRadioInternal, + .spi_bus_handle = &furi_hal_spi_bus_handle_subghz, + .cc1101_g0_pin = &gpio_cc1101_g0, }; +bool furi_hal_subghz_set_radio_type(SubGhzRadioType state) { + furi_hal_subghz.radio_type = state; + furi_hal_spi_bus_handle_deinit(furi_hal_subghz.spi_bus_handle); + if(state) { + furi_hal_subghz.spi_bus_handle = &furi_hal_spi_bus_handle_subghz_ext; + furi_hal_subghz.cc1101_g0_pin = &gpio_cc1101_g0_ext; + } else { + furi_hal_subghz.spi_bus_handle = &furi_hal_spi_bus_handle_subghz; + furi_hal_subghz.cc1101_g0_pin = &gpio_cc1101_g0; + } + furi_hal_spi_bus_handle_init(furi_hal_subghz.spi_bus_handle); + furi_hal_subghz_init_check(); + return true; +} + +SubGhzRadioType furi_hal_subghz_get_radio_type(void) { + return furi_hal_subghz.radio_type; +} + void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin) { furi_hal_subghz.async_mirror_pin = pin; } -void furi_hal_subghz_init() { +void furi_hal_subghz_init(void) { + furi_hal_subghz_init_check(); +} + +void furi_hal_subghz_enable_ext_power(void) { + if(furi_hal_subghz.radio_type != SubGhzRadioInternal && !furi_hal_power_is_otg_enabled()) { + furi_hal_power_enable_otg(); + } +} + +void furi_hal_subghz_disable_ext_power(void) { + if(furi_hal_subghz.radio_type != SubGhzRadioInternal && !last_OTG_state) { + furi_hal_power_disable_otg(); + } +} + +bool furi_hal_subghz_check_radio(void) { + bool result = true; + + furi_hal_subghz_enable_ext_power(); + + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + uint8_t ver = cc1101_get_version(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); + + if((ver != 0) && (ver != 255)) { + FURI_LOG_D(TAG, "Radio check ok"); + } else { + FURI_LOG_D(TAG, "Radio check failed"); + furi_hal_subghz_disable_ext_power(); + result = false; + } + return result; +} + +bool furi_hal_subghz_init_check(void) { + bool result = true; + furi_assert(furi_hal_subghz.state == SubGhzStateInit); furi_hal_subghz.state = SubGhzStateIdle; furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + last_OTG_state = furi_hal_power_is_otg_enabled(); + furi_hal_subghz_enable_ext_power(); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); #ifdef FURI_HAL_SUBGHZ_TX_GPIO furi_hal_gpio_init(&FURI_HAL_SUBGHZ_TX_GPIO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); #endif // Reset - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_reset(furi_hal_subghz.spi_bus_handle); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); // Prepare GD0 for power on self test - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { + // GD0 low + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); + uint32_t test_start_time = furi_get_tick(); + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin) != false && result) { + if(furi_get_tick() - test_start_time > INIT_TIMEOUT) { + result = false; + } + } - // GD0 low - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW); - while(furi_hal_gpio_read(&gpio_cc1101_g0) != false) - ; - - // GD0 high - cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); - while(furi_hal_gpio_read(&gpio_cc1101_g0) != true) - ; + // GD0 high + cc1101_write_reg( + furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); + test_start_time = furi_get_tick(); + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin) != true && result) { + if(furi_get_tick() - test_start_time > INIT_TIMEOUT) { + result = false; + } + } + } else { + // GD0 low + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); + while(furi_hal_gpio_read(&gpio_cc1101_g0) != false) + ; + // GD0 high + cc1101_write_reg( + furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); + while(furi_hal_gpio_read(&gpio_cc1101_g0) != true) + ; + } // Reset GD0 to floating state - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); // RF switches furi_hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW); // Go to sleep - cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); + cc1101_shutdown(furi_hal_subghz.spi_bus_handle); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - FURI_LOG_I(TAG, "Init OK"); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); + + if(result) { + FURI_LOG_I(TAG, "Init OK"); + } else { + FURI_LOG_E(TAG, "Failed to initialization"); + furi_hal_subghz_disable_ext_power(); + } + return result; } void furi_hal_subghz_sleep() { furi_assert(furi_hal_subghz.state == SubGhzStateIdle); - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); + cc1101_shutdown(furi_hal_subghz.spi_bus_handle); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); + + furi_hal_subghz_disable_ext_power(); furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; } void furi_hal_subghz_dump_state() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); printf( "[furi_hal_subghz] cc1101 chip %d, version %d\r\n", - cc1101_get_partnumber(&furi_hal_spi_bus_handle_subghz), - cc1101_get_version(&furi_hal_spi_bus_handle_subghz)); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_get_partnumber(furi_hal_subghz.spi_bus_handle), + cc1101_get_version(furi_hal_subghz.spi_bus_handle)); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { @@ -137,15 +230,15 @@ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { //load config - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_reset(furi_hal_subghz.spi_bus_handle); uint32_t i = 0; uint8_t pa[8] = {0}; while(preset_data[i]) { - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, preset_data[i], preset_data[i + 1]); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, preset_data[i], preset_data[i + 1]); i += 2; } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); //load pa table memcpy(&pa[0], &preset_data[i + 2], 8); @@ -167,48 +260,48 @@ void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { } void furi_hal_subghz_load_registers(uint8_t* data) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_reset(furi_hal_subghz.spi_bus_handle); uint32_t i = 0; while(data[i]) { - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i], data[i + 1]); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, data[i], data[i + 1]); i += 2; } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_load_patable(const uint8_t data[8]) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_set_pa_table(&furi_hal_spi_bus_handle_subghz, data); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_set_pa_table(furi_hal_subghz.spi_bus_handle, data); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_FIFO, size); - cc1101_write_fifo(&furi_hal_spi_bus_handle_subghz, data, size); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_flush_tx(furi_hal_subghz.spi_bus_handle); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_FIFO, size); + cc1101_write_fifo(furi_hal_subghz.spi_bus_handle, data, size); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_flush_rx() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_rx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_flush_rx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_flush_tx() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_flush_tx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } bool furi_hal_subghz_rx_pipe_not_empty() { CC1101RxBytes status[1]; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); cc1101_read_reg( - &furi_hal_spi_bus_handle_subghz, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_subghz.spi_bus_handle, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); // TODO: you can add a buffer overflow flag if needed if(status->NUM_RXBYTES > 0) { return true; @@ -218,10 +311,10 @@ bool furi_hal_subghz_rx_pipe_not_empty() { } bool furi_hal_subghz_is_rx_data_crc_valid() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); uint8_t data[1]; - cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_read_reg(furi_hal_subghz.spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); if(((data[0] >> 7) & 0x01)) { return true; } else { @@ -230,51 +323,52 @@ bool furi_hal_subghz_is_rx_data_crc_valid() { } void furi_hal_subghz_read_packet(uint8_t* data, uint8_t* size) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_read_fifo(&furi_hal_spi_bus_handle_subghz, data, size); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_read_fifo(furi_hal_subghz.spi_bus_handle, data, size); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_shutdown() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); // Reset and shutdown - cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_shutdown(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); + furi_hal_subghz_disable_ext_power(); } void furi_hal_subghz_reset() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle); + cc1101_reset(furi_hal_subghz.spi_bus_handle); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_idle() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_rx() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_switch_to_rx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } bool furi_hal_subghz_tx() { - // if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_tx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_switch_to_tx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); return true; } float furi_hal_subghz_get_rssi() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - int32_t rssi_dec = cc1101_get_rssi(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + int32_t rssi_dec = cc1101_get_rssi(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); float rssi = rssi_dec; if(rssi_dec >= 128) { @@ -287,18 +381,18 @@ float furi_hal_subghz_get_rssi() { } uint8_t furi_hal_subghz_get_lqi() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); uint8_t data[1]; - cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_read_reg(furi_hal_subghz.spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); return data[0] & 0x7F; } /* Modified by @tkerby & MX to the full YARD Stick One extended range of 281-361 MHz, 378-481 MHz, and 749-962 MHz. - These changes are at your own risk. The PLL may not lock and FZ devs have warned of possible damage - Set flag use_ext_range_at_own_risk in extend_range.txt to use + These changes are at your own risk. The PLL may not lock and FZ devs have warned of possible damage! */ + bool furi_hal_subghz_is_frequency_valid(uint32_t value) { if(!(value >= 281000000 && value <= 361000000) && !(value >= 378000000 && value <= 481000000) && @@ -325,113 +419,72 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { } bool furi_hal_subghz_is_tx_allowed(uint32_t value) { - //checking regional settings bool is_extended = false; - bool is_allowed = false; + // TODO: !!! Move file check to another place Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_existing(fff_data_file, "/ext/subghz/assets/extend_range.txt")) { - flipper_format_read_bool(fff_data_file, "use_ext_range_at_own_risk", &is_extended, 1); - flipper_format_read_bool(fff_data_file, "ignore_default_tx_region", &is_allowed, 1); + + if(flipper_format_file_open_existing(fff_data_file, "/ext/subghz/assets/dangerous_settings")) { + flipper_format_read_bool( + fff_data_file, "yes_i_want_to_destroy_my_flipper", &is_extended, 1); } + flipper_format_free(fff_data_file); furi_record_close(RECORD_STORAGE); - switch(furi_hal_version_get_hw_region()) { - case FuriHalVersionRegionEuRu: - //433,05..434,79; 868,15..868,55 - if(!(value >= 433050000 && value <= 434790000) && - !(value >= 868150000 && value <= 868550000)) { - } else { - is_allowed = true; - } - break; - case FuriHalVersionRegionUsCaAu: - //304,10..321,95; 433,05..434,79; 915,00..928,00 - if(!(value >= 304100000 && value <= 321950000) && - !(value >= 433050000 && value <= 434790000) && - !(value >= 915000000 && value <= 928000000)) { - } else { - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - if(value <= 321950000 && - ((furi_hal_subghz.preset == FuriHalSubGhzPresetOok270Async) || - (furi_hal_subghz.preset == FuriHalSubGhzPresetOok650Async))) { - furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable_au); - } - } - is_allowed = true; - } - break; - case FuriHalVersionRegionJp: - //312,00..315,25; 920,50..923,50 - if(!(value >= 312000000 && value <= 315250000) && - !(value >= 920500000 && value <= 923500000)) { - } else { - is_allowed = true; - } - break; - - default: - is_allowed = true; - break; - } - // No flag - test original range, flag set, test extended range - if(!(value >= 299999755 && value <= 348000335) && + if(!(value >= 299999755 && value <= 350000335) && !(value >= 386999938 && value <= 464000000) && !(value >= 778999847 && value <= 928000000) && !(is_extended)) { - FURI_LOG_I(TAG, "Frequency blocked - outside standard range"); - is_allowed = false; + FURI_LOG_I(TAG, "Frequency blocked - outside default range"); + return false; } else if( !(value >= 281000000 && value <= 361000000) && !(value >= 378000000 && value <= 481000000) && !(value >= 749000000 && value <= 962000000) && is_extended) { - FURI_LOG_I(TAG, "Frequency blocked - outside extended range"); - is_allowed = false; + FURI_LOG_I(TAG, "Frequency blocked - outside dangerous range"); + return false; } - return is_allowed; + + return true; } uint32_t furi_hal_subghz_set_frequency(uint32_t value) { - if(furi_hal_region_is_frequency_allowed(value)) { - furi_hal_subghz.regulation = SubGhzRegulationTxRx; - } else { - furi_hal_subghz.regulation = SubGhzRegulationTxRx; - } + furi_hal_subghz.regulation = SubGhzRegulationTxRx; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - uint32_t real_frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, value); - cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + uint32_t real_frequency = cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, value); + cc1101_calibrate(furi_hal_subghz.spi_bus_handle); while(true) { - CC1101Status status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); + CC1101Status status = cc1101_get_status(furi_hal_subghz.spi_bus_handle); if(status.STATE == CC1101StateIDLE) break; } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); return real_frequency; } void furi_hal_subghz_set_path(FuriHalSubGhzPath path) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); if(path == FuriHalSubGhzPath433) { furi_hal_gpio_write(&gpio_rf_sw_0, 0); cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); + furi_hal_subghz.spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); } else if(path == FuriHalSubGhzPath315) { furi_hal_gpio_write(&gpio_rf_sw_0, 1); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW); } else if(path == FuriHalSubGhzPath868) { furi_hal_gpio_write(&gpio_rf_sw_0, 1); cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); + furi_hal_subghz.spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); } else if(path == FuriHalSubGhzPathIsolate) { furi_hal_gpio_write(&gpio_rf_sw_0, 0); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW); } else { furi_crash("SubGhz: Incorrect path during set."); } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } static bool furi_hal_subghz_start_debug() { @@ -462,31 +515,53 @@ volatile FuriHalSubGhzCaptureCallback furi_hal_subghz_capture_callback = NULL; volatile void* furi_hal_subghz_capture_callback_context = NULL; static void furi_hal_subghz_capture_ISR() { - // Channel 1 - if(LL_TIM_IsActiveFlag_CC1(TIM2)) { - LL_TIM_ClearFlag_CC1(TIM2); - furi_hal_subghz_capture_delta_duration = LL_TIM_IC_GetCaptureCH1(TIM2); - if(furi_hal_subghz_capture_callback) { - if(furi_hal_subghz.async_mirror_pin != NULL) - furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); + if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { + if(!furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); - furi_hal_subghz_capture_callback( - true, - furi_hal_subghz_capture_delta_duration, - (void*)furi_hal_subghz_capture_callback_context); + furi_hal_subghz_capture_callback( + true, TIM2->CNT, (void*)furi_hal_subghz_capture_callback_context); + } + } else { + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); + + furi_hal_subghz_capture_callback( + false, TIM2->CNT, (void*)furi_hal_subghz_capture_callback_context); + } } - } - // Channel 2 - if(LL_TIM_IsActiveFlag_CC2(TIM2)) { - LL_TIM_ClearFlag_CC2(TIM2); - if(furi_hal_subghz_capture_callback) { - if(furi_hal_subghz.async_mirror_pin != NULL) - furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); + //Forced correction for improved accuracy + TIM2->CNT = 9; + } else { + // Channel 1 + if(LL_TIM_IsActiveFlag_CC1(TIM2)) { + LL_TIM_ClearFlag_CC1(TIM2); + furi_hal_subghz_capture_delta_duration = LL_TIM_IC_GetCaptureCH1(TIM2); + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); - furi_hal_subghz_capture_callback( - false, - LL_TIM_IC_GetCaptureCH2(TIM2) - furi_hal_subghz_capture_delta_duration, - (void*)furi_hal_subghz_capture_callback_context); + furi_hal_subghz_capture_callback( + true, + furi_hal_subghz_capture_delta_duration, + (void*)furi_hal_subghz_capture_callback_context); + } + } + // Channel 2 + if(LL_TIM_IsActiveFlag_CC2(TIM2)) { + LL_TIM_ClearFlag_CC2(TIM2); + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); + + furi_hal_subghz_capture_callback( + false, + LL_TIM_IC_GetCaptureCH2(TIM2) - furi_hal_subghz_capture_delta_duration, + (void*)furi_hal_subghz_capture_callback_context); + } } } } @@ -498,47 +573,64 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* furi_hal_subghz_capture_callback = callback; furi_hal_subghz_capture_callback_context = context; - furi_hal_gpio_init_ex( - &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, + GpioModeInterruptRiseFall, + GpioPullUp, + GpioSpeedVeryHigh); + furi_hal_gpio_add_int_callback( + furi_hal_subghz.cc1101_g0_pin, + furi_hal_subghz_capture_ISR, + furi_hal_subghz_capture_callback); + furi_hal_gpio_enable_int_callback(furi_hal_subghz.cc1101_g0_pin); + } else { + furi_hal_gpio_init_ex( + &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + } // Timer: base LL_TIM_InitTypeDef TIM_InitStruct = {0}; TIM_InitStruct.Prescaler = 64 - 1; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 0x7FFFFFFE; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; // Clock division for capture filter LL_TIM_Init(TIM2, &TIM_InitStruct); // Timer: advanced LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_DisableARRPreload(TIM2); - LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI2FP2); - LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET); - LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); - LL_TIM_EnableMasterSlaveMode(TIM2); + if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { + LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI2FP2); + LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET); + LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); + LL_TIM_EnableMasterSlaveMode(TIM2); + } LL_TIM_DisableDMAReq_TRIG(TIM2); LL_TIM_DisableIT_TRIG(TIM2); - // Timer: channel 1 indirect - LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI); - LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); - LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); + if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { + // Timer: channel 1 indirect + LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI); + LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING); + LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); - // Timer: channel 2 direct - LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); - LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); - LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV32_N8); + // Timer: channel 2 direct + LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); + LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV32_N8); - // ISR setup - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_capture_ISR, NULL); + // ISR setup + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_capture_ISR, NULL); - // Interrupts and channels - LL_TIM_EnableIT_CC1(TIM2); - LL_TIM_EnableIT_CC2(TIM2); - LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); - LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); + // Interrupts and channels + LL_TIM_EnableIT_CC1(TIM2); + LL_TIM_EnableIT_CC2(TIM2); + LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); + LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); + } // Start timer LL_TIM_SetCounter(TIM2, 0); @@ -549,6 +641,9 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* // Switch to RX furi_hal_subghz_rx(); + + // Clear the variable after the end of the session + furi_hal_subghz_capture_delta_duration = 0; } void furi_hal_subghz_stop_async_rx() { @@ -565,9 +660,11 @@ void furi_hal_subghz_stop_async_rx() { furi_hal_subghz_stop_debug(); FURI_CRITICAL_EXIT(); - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + } - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } typedef struct { @@ -601,8 +698,8 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { *buffer = 0; buffer++; samples--; - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_HT(SUBGHZ_DMA_CH1_DEF); + LL_DMA_DisableIT_TC(SUBGHZ_DMA_CH1_DEF); LL_TIM_EnableIT_UPDATE(TIM2); break; } else { @@ -643,17 +740,22 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { static void furi_hal_subghz_async_tx_dma_isr() { furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); - if(LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); + +#if SUBGHZ_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_HT1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); + if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } +#else +#error Update this code. Would you kindly? +#endif } static void furi_hal_subghz_async_tx_timer_isr() { @@ -662,11 +764,12 @@ static void furi_hal_subghz_async_tx_timer_isr() { if(LL_TIM_GetAutoReload(TIM2) == 0) { if(furi_hal_subghz.state == SubGhzStateAsyncTx) { furi_hal_subghz.state = SubGhzStateAsyncTxLast; - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) { furi_hal_subghz.state = SubGhzStateAsyncTxEnd; //forcibly pulls the pin to the ground so that there is no carrier - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullDown, GpioSpeedLow); LL_TIM_DisableCounter(TIM2); } else { furi_crash(NULL); @@ -680,7 +783,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* furi_assert(callback); //If transmission is prohibited by regional settings - // if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; + if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; furi_hal_subghz_async_tx.callback = callback; furi_hal_subghz_async_tx.callback_context = context; @@ -693,9 +796,19 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* furi_hal_subghz_async_tx.buffer = malloc(API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); - // Connect CC1101_GD0 to TIM2 as output - furi_hal_gpio_init_ex( - &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn1TIM2); + if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, true); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else { + // Connect CC1101_GD0 to TIM2 as output + furi_hal_gpio_init_ex( + &gpio_cc1101_g0, + GpioModeAltFunctionPushPull, + GpioPullDown, + GpioSpeedLow, + GpioAltFn1TIM2); + } // Configure DMA LL_DMA_InitTypeDef dma_config = {0}; @@ -710,11 +823,11 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_subghz_async_tx_dma_isr, NULL); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); + furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL); + LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF); + LL_DMA_EnableIT_HT(SUBGHZ_DMA_CH1_DEF); + LL_DMA_EnableChannel(SUBGHZ_DMA_CH1_DEF); // Configure TIM2 LL_TIM_InitTypeDef TIM_InitStruct = {0}; @@ -755,9 +868,20 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); - // Start debug - if(furi_hal_subghz_start_debug()) { - const GpioPin* gpio = furi_hal_subghz.async_mirror_pin; + //Signal generation for external module + + // Start debug (and speaker) + furi_hal_subghz_start_debug(); + + const GpioPin* gpio = furi_hal_subghz.cc1101_g0_pin; + + if((furi_hal_subghz.async_mirror_pin != NULL) && + (furi_hal_subghz.radio_type == SubGhzRadioInternal)) { + gpio = furi_hal_subghz.async_mirror_pin; + } + if(((furi_hal_subghz.async_mirror_pin != NULL) && + (furi_hal_subghz.radio_type == SubGhzRadioInternal)) || + (furi_hal_subghz.radio_type == SubGhzRadioExternal)) { furi_hal_subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; furi_hal_subghz_debug_gpio_buff[1] = gpio->pin; @@ -772,9 +896,9 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.NbData = 2; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config); + LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2); + LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF); } return true; @@ -792,9 +916,9 @@ void furi_hal_subghz_stop_async_tx() { // Shutdown radio furi_hal_subghz_idle(); -#ifdef FURI_HAL_SUBGHZ_TX_GPIO - furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, false); -#endif + if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); + } // Deinitialize Timer FURI_CRITICAL_ENTER(); @@ -802,16 +926,20 @@ void furi_hal_subghz_stop_async_tx() { furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); // Deinitialize DMA - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DeInit(SUBGHZ_DMA_CH1_DEF); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); + furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, NULL, NULL); // Deinitialize GPIO - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); // Stop debug - if(furi_hal_subghz_stop_debug()) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + furi_hal_subghz_stop_debug(); + + if(((furi_hal_subghz.async_mirror_pin != NULL) && + (furi_hal_subghz.radio_type == SubGhzRadioInternal)) || + (furi_hal_subghz.radio_type == SubGhzRadioExternal)) { + LL_DMA_DisableChannel(SUBGHZ_DMA_CH2_DEF); } FURI_CRITICAL_EXIT(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index b3319e226..b19a71f9a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -61,6 +62,25 @@ typedef enum { SubGhzRegulationTxRx, /**TxRx*/ } SubGhzRegulation; +/** SubGhz radio types */ +typedef enum { + SubGhzRadioInternal, + SubGhzRadioExternal, +} SubGhzRadioType; + +/** Structure for accessing SubGhz settings*/ +typedef struct { + volatile SubGhzState state; + volatile SubGhzRegulation regulation; + volatile FuriHalSubGhzPreset preset; + const GpioPin* async_mirror_pin; + SubGhzRadioType radio_type; + FuriHalSpiBusHandle* spi_bus_handle; + const GpioPin* cc1101_g0_pin; +} FuriHalSubGhz; + +extern volatile FuriHalSubGhz furi_hal_subghz; + /* Mirror RX/TX async modulation signal to specified pin * * @warning Configures pin to output mode. Make sure it is not connected @@ -76,6 +96,13 @@ void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin); */ void furi_hal_subghz_init(); +/** Initialize and switch to power save mode Used by internal API-HAL + * initialization routine Can be used to reinitialize device to safe state and + * send it to sleep + * @return true if initialisation is successfully + */ +bool furi_hal_subghz_init_check(void); + /** Send device to sleep mode */ void furi_hal_subghz_sleep(); @@ -258,6 +285,30 @@ bool furi_hal_subghz_is_async_tx_complete(); */ void furi_hal_subghz_stop_async_tx(); +/** Switching between internal and external radio + * @param state SubGhzRadioInternal or SubGhzRadioExternal + * @return true if switching is successful + */ +bool furi_hal_subghz_set_radio_type(SubGhzRadioType state); + +/** Get current radio + * @return SubGhzRadioInternal or SubGhzRadioExternal + */ +SubGhzRadioType furi_hal_subghz_get_radio_type(void); + +/** Check for a radio module + * @return true if check is successful + */ +bool furi_hal_subghz_check_radio(void); + +/** Turn on the power of the external radio module + */ +void furi_hal_subghz_enable_ext_power(void); + +/** Turn off the power of the external radio module + */ +void furi_hal_subghz_disable_ext_power(void); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h b/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h index 5ea17b6dd..b2b5760fd 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h @@ -273,16 +273,6 @@ static const uint8_t furi_hal_subghz_preset_ook_async_patable[8] = { 0x00, 0x00}; -static const uint8_t furi_hal_subghz_preset_ook_async_patable_au[8] = { - 0x00, - 0x37, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = { 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 0x00, diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 6d091114b..8fbec94ad 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -11,6 +11,7 @@ env.Append( File("subghz_tx_rx_worker.h"), File("transmitter.h"), File("registry.h"), + File("protocols/protocol_items.h"), File("protocols/raw.h"), File("blocks/const.h"), File("blocks/decoder.h"), diff --git a/lib/subghz/blocks/encoder.c b/lib/subghz/blocks/encoder.c index e325be21c..49ec4f177 100644 --- a/lib/subghz/blocks/encoder.c +++ b/lib/subghz/blocks/encoder.c @@ -55,4 +55,4 @@ size_t subghz_protocol_blocks_get_upload_from_bit_array( upload[size_upload++] = level_duration_make( subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); return size_upload; -} \ No newline at end of file +} diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 94114676d..3d59adc82 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -100,7 +100,7 @@ bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperForma FURI_LOG_E(TAG, "Missing Bit"); break; } - instance->data_count_bit = (uint8_t)temp_data; + instance->data_count_bit = (uint16_t)temp_data; uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { diff --git a/lib/subghz/blocks/generic.h b/lib/subghz/blocks/generic.h index 8839c0b54..e69de8b4f 100644 --- a/lib/subghz/blocks/generic.h +++ b/lib/subghz/blocks/generic.h @@ -20,7 +20,7 @@ struct SubGhzBlockGeneric { uint64_t data; uint64_t data_2; uint32_t serial; - uint8_t data_count_bit; + uint16_t data_count_bit; uint8_t btn; uint32_t cnt; uint8_t cnt_2; diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c index 3fef76af2..67e0467ee 100644 --- a/lib/subghz/protocols/bin_raw.c +++ b/lib/subghz/protocols/bin_raw.c @@ -111,7 +111,7 @@ const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = { const SubGhzProtocol subghz_protocol_bin_raw = { .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME, - .type = SubGhzProtocolTypeStatic, + .type = SubGhzProtocolTypeBinRAW, #ifdef BIN_RAW_DEBUG .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | @@ -1117,4 +1117,4 @@ void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* outpu } furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te); -} \ No newline at end of file +} diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c index 2c8de0d2d..5f2a83d77 100644 --- a/lib/subghz/protocols/kinggates_stylo_4k.c +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -23,7 +23,6 @@ struct SubGhzProtocolDecoderKingGates_stylo_4k { SubGhzBlockDecoder decoder; SubGhzBlockGeneric generic; - uint64_t data; uint16_t header_count; SubGhzKeystore* keystore; }; @@ -33,6 +32,7 @@ struct SubGhzProtocolEncoderKingGates_stylo_4k { SubGhzProtocolBlockEncoder encoder; SubGhzBlockGeneric generic; + SubGhzKeystore* keystore; }; typedef enum { @@ -57,23 +57,268 @@ const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = { }; const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = { - .alloc = NULL, - .free = NULL, + .alloc = subghz_protocol_encoder_kinggates_stylo_4k_alloc, + .free = subghz_protocol_encoder_kinggates_stylo_4k_free, - .deserialize = NULL, - .stop = NULL, - .yield = NULL, + .deserialize = subghz_protocol_encoder_kinggates_stylo_4k_deserialize, + .stop = subghz_protocol_encoder_kinggates_stylo_4k_stop, + .yield = subghz_protocol_encoder_kinggates_stylo_4k_yield, }; const SubGhzProtocol subghz_protocol_kinggates_stylo_4k = { .name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME, .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, .decoder = &subghz_protocol_kinggates_stylo_4k_decoder, .encoder = &subghz_protocol_kinggates_stylo_4k_encoder, }; +// +// Encoder +// + +// Pre define function +static void subghz_protocol_kinggates_stylo_4k_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore); + +void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderKingGates_stylo_4k* instance = + malloc(sizeof(SubGhzProtocolEncoderKingGates_stylo_4k)); + + instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + free(instance->encoder.upload); + free(instance); +} + +void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context) { + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context) { + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +/** + * Key generation from simple data + * @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k* instance + * @param btn Button number, 4 bit + */ +static bool subghz_protocol_kinggates_stylo_4k_gen_data( + SubGhzProtocolEncoderKingGates_stylo_4k* instance, + uint8_t btn) { + UNUSED(btn); + uint32_t hop = subghz_protocol_blocks_reverse_key(instance->generic.data_2 >> 4, 32); + uint64_t fix = subghz_protocol_blocks_reverse_key(instance->generic.data, 53); + int res = 0; + uint32_t decrypt = 0; + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k"); + if(res == 0) { + //Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + break; + } + } + instance->generic.cnt = decrypt & 0xFFFF; + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + instance->generic.btn = (fix >> 17) & 0x0F; + instance->generic.serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); + + uint32_t data = (decrypt & 0xFFFF0000) | instance->generic.cnt; + + uint64_t encrypt = 0; + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k"); + if(res == 0) { + //Simple Learning + encrypt = subghz_protocol_keeloq_common_encrypt(data, manufacture_code->key); + encrypt = subghz_protocol_blocks_reverse_key(encrypt, 32); + instance->generic.data_2 = encrypt << 4; + return true; + } + } + + return false; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @return true On success + */ +static bool subghz_protocol_encoder_kinggates_stylo_4k_get_upload( + SubGhzProtocolEncoderKingGates_stylo_4k* instance, + uint8_t btn) { + furi_assert(instance); + + // Gen new key + if(subghz_protocol_kinggates_stylo_4k_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + // Start + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)9500); + + // Send header + for(uint8_t i = 12; i > 0; i--) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + + // After header + instance->encoder.upload[index - 1].duration = + (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 2; + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short * 2); + + // Send key fix + for(uint8_t i = 53; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + } + + // Send key hop + for(uint8_t i = 36; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + } + + // Set upload size after generating upload, fix it later + + instance->encoder.size_upload = index; + + return true; +} + +bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + subghz_protocol_kinggates_stylo_4k_remote_controller( + &instance->generic, instance->keystore); + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i]; + } + + subghz_protocol_encoder_kinggates_stylo_4k_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +// +// Decoder +// void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { SubGhzProtocolDecoderKingGates_stylo_4k* instance = malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k)); @@ -130,7 +375,7 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) { instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; instance->decoder.decode_data = 0; - instance->data = 0; + instance->generic.data_2 = 0; instance->decoder.decode_count_bit = 0; instance->header_count = 0; } @@ -140,8 +385,8 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) { if(instance->decoder.decode_count_bit == subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { - instance->generic.data = instance->data; - instance->data = instance->decoder.decode_data; + instance->generic.data = instance->generic.data_2; + instance->generic.data_2 = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; if(instance->base.callback) @@ -150,7 +395,7 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; instance->decoder.decode_data = 0; - instance->data = 0; + instance->generic.data_2 = 0; instance->decoder.decode_count_bit = 0; instance->header_count = 0; break; @@ -185,7 +430,7 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, instance->header_count = 0; } if(instance->decoder.decode_count_bit == 53) { - instance->data = instance->decoder.decode_data; + instance->generic.data_2 = instance->decoder.decode_data; instance->decoder.decode_data = 0; } } else { @@ -199,11 +444,11 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, /** * Analysis of received data * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param file_name Full path to rainbow table the file + * @param data Input encrypted data + * @param keystore Pointer to a SubGhzKeystore* instance */ static void subghz_protocol_kinggates_stylo_4k_remote_controller( SubGhzBlockGeneric* instance, - uint64_t data, SubGhzKeystore* keystore) { /** * 9500us 12*(400/400) 2200/800|1-bit|0-bit| @@ -226,7 +471,7 @@ static void subghz_protocol_kinggates_stylo_4k_remote_controller( * */ - uint32_t hop = subghz_protocol_blocks_reverse_key(data >> 4, 32); + uint32_t hop = subghz_protocol_blocks_reverse_key(instance->data_2 >> 4, 32); uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53); bool ret = false; uint32_t decrypt = 0; @@ -270,7 +515,7 @@ bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> (i * 8)) & 0xFF; } if(res && !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { @@ -306,8 +551,9 @@ bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( FURI_LOG_E(TAG, "Missing Data"); break; } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { - instance->data = instance->data << 8 | key_data[i]; + instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i]; } ret = true; } while(false); @@ -317,8 +563,7 @@ bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - subghz_protocol_kinggates_stylo_4k_remote_controller( - &instance->generic, instance->data, instance->keystore); + subghz_protocol_kinggates_stylo_4k_remote_controller(&instance->generic, instance->keystore); furi_string_cat_printf( output, @@ -328,9 +573,9 @@ void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriSt "Cnt:0x%04lX\r\n", instance->generic.protocol_name, instance->generic.data, - instance->data, + instance->generic.data_2, instance->generic.data_count_bit, instance->generic.serial, instance->generic.btn, instance->generic.cnt); -} +} \ No newline at end of file diff --git a/lib/subghz/protocols/kinggates_stylo_4k.h b/lib/subghz/protocols/kinggates_stylo_4k.h index c9f1cf380..9717f6715 100644 --- a/lib/subghz/protocols/kinggates_stylo_4k.h +++ b/lib/subghz/protocols/kinggates_stylo_4k.h @@ -10,6 +10,42 @@ extern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder; extern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder; extern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k; +/** + * Allocate SubGhzProtocolEncoderKingGates_stylo_4k. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderKingGates_stylo_4k* pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context); + /** * Allocate SubGhzProtocolDecoderKingGates_stylo_4k. * @param environment Pointer to a SubGhzEnvironment instance diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 96717e56f..74244c5ff 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -36,13 +36,13 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_intertechno_v3, &subghz_protocol_clemsa, &subghz_protocol_ansonic, - &subghz_protocol_pocsag, &subghz_protocol_smc5326, &subghz_protocol_holtek_th12x, &subghz_protocol_linear_delta3, &subghz_protocol_dooya, &subghz_protocol_alutech_at_4n, &subghz_protocol_kinggates_stylo_4k, + &subghz_protocol_bin_raw, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 362b0459a..b7e082bf5 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -37,12 +37,12 @@ #include "intertechno_v3.h" #include "clemsa.h" #include "ansonic.h" -#include "pocsag.h" #include "smc5326.h" #include "holtek_ht12x.h" #include "dooya.h" #include "alutech_at_4n.h" #include "kinggates_stylo_4k.h" +#include "bin_raw.h" #ifdef __cplusplus extern "C" { diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index 393d7f360..01a229047 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -8,16 +8,11 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#include #include #include -#include #define TAG "SubGhzProtocolRAW" #define SUBGHZ_DOWNLOAD_MAX_SIZE 512 -#define SUBGHZ_AUTO_DETECT_DOWNLOAD_MAX_SIZE 2048 -#define SUBGHZ_AUTO_DETECT_RAW_THRESHOLD -72.0f -#define SUBGHZ_AUTO_DETECT_RAW_POSTROLL_FRAMES 40 static const SubGhzBlockConst subghz_protocol_raw_const = { .te_short = 50, @@ -29,8 +24,6 @@ static const SubGhzBlockConst subghz_protocol_raw_const = { struct SubGhzProtocolDecoderRAW { SubGhzProtocolDecoderBase base; - SubGhzBlockDecoder decoder; - int32_t* upload_raw; uint16_t ind_write; Storage* storage; @@ -39,10 +32,6 @@ struct SubGhzProtocolDecoderRAW { FuriString* file_name; size_t sample_write; bool last_level; - bool auto_mode; - bool has_rssi_above_threshold; - int rssi_threshold; - uint8_t postroll_frames; bool pause; }; @@ -67,8 +56,8 @@ const SubGhzProtocolDecoder subghz_protocol_raw_decoder = { .feed = subghz_protocol_decoder_raw_feed, .reset = subghz_protocol_decoder_raw_reset, - .get_hash_data = subghz_protocol_decoder_raw_get_hash_data, - .serialize = subghz_protocol_decoder_raw_serialize, + .get_hash_data = NULL, + .serialize = NULL, .deserialize = subghz_protocol_decoder_raw_deserialize, .get_string = subghz_protocol_decoder_raw_get_string, }; @@ -213,36 +202,6 @@ void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance) { instance->file_is_open = RAWFileIsOpenClose; } -void subghz_protocol_decoder_raw_set_rssi_threshold(void* context, int rssi_threshold) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - - FURI_LOG_D(TAG, "RSSI set: (%d)", rssi_threshold); - - instance->rssi_threshold = rssi_threshold; - - subghz_protocol_decoder_raw_reset(context); -} - -void subghz_protocol_decoder_raw_set_auto_mode(void* context, bool auto_mode) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - instance->auto_mode = auto_mode; - - if(auto_mode) { - if(instance->upload_raw == NULL) { - instance->upload_raw = malloc(SUBGHZ_AUTO_DETECT_DOWNLOAD_MAX_SIZE * sizeof(int32_t)); - } - } else { - if(instance->upload_raw != NULL) { - free(instance->upload_raw); - instance->upload_raw = NULL; - } - } - - subghz_protocol_decoder_raw_reset(context); -} - void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause) { furi_assert(instance); @@ -263,8 +222,6 @@ void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment) { instance->ind_write = 0; instance->last_level = false; instance->file_is_open = RAWFileIsOpenClose; - instance->postroll_frames = 0; - instance->rssi_threshold = SUBGHZ_AUTO_DETECT_RAW_THRESHOLD; instance->file_name = furi_string_alloc(); return instance; @@ -274,10 +231,6 @@ void subghz_protocol_decoder_raw_free(void* context) { furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; furi_string_free(instance->file_name); - if(instance->upload_raw != NULL) { - free(instance->upload_raw); - instance->upload_raw = NULL; - } free(instance); } @@ -285,66 +238,23 @@ void subghz_protocol_decoder_raw_reset(void* context) { furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; instance->ind_write = 0; - instance->has_rssi_above_threshold = false; instance->last_level = false; - instance->postroll_frames = 0; -} - -bool subghz_protocol_decoder_raw_write_data(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - - bool wrote_data = false; - - if(instance->last_level != level) { - instance->last_level = (level ? true : false); - instance->upload_raw[instance->ind_write++] = (level ? duration : -duration); - subghz_protocol_blocks_add_bit(&instance->decoder, (level) ? 1 : 0); - wrote_data = true; - } - - if(instance->ind_write == SUBGHZ_AUTO_DETECT_DOWNLOAD_MAX_SIZE) { - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - - return false; - } - - return wrote_data; } void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration) { furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; - if(instance->upload_raw != NULL && !instance->pause && - duration > subghz_protocol_raw_const.te_short) { - if(instance->auto_mode) { - float rssi = furi_hal_subghz_get_rssi(); - - if(rssi >= instance->rssi_threshold) { - subghz_protocol_decoder_raw_write_data(context, level, duration); - instance->has_rssi_above_threshold = true; - instance->postroll_frames = 0; - } else if(instance->has_rssi_above_threshold) { - subghz_protocol_decoder_raw_write_data(instance, level, duration); - instance->postroll_frames++; - - if(instance->postroll_frames >= SUBGHZ_AUTO_DETECT_RAW_POSTROLL_FRAMES) { - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - } - } else { + if(!instance->pause && (instance->upload_raw != NULL)) { + if(duration > subghz_protocol_raw_const.te_short) { if(instance->last_level != level) { instance->last_level = (level ? true : false); instance->upload_raw[instance->ind_write++] = (level ? duration : -duration); - subghz_protocol_blocks_add_bit(&instance->decoder, (level) ? 1 : 0); } + } - if(instance->ind_write == SUBGHZ_DOWNLOAD_MAX_SIZE) { - subghz_protocol_raw_save_to_file_write(instance); - } + if(instance->ind_write == SUBGHZ_DOWNLOAD_MAX_SIZE) { + subghz_protocol_raw_save_to_file_write(instance); } } } @@ -357,13 +267,6 @@ bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipp return true; } -uint8_t subghz_protocol_decoder_raw_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output) { furi_assert(context); //SubGhzProtocolDecoderRAW* instance = context; @@ -382,13 +285,6 @@ void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment) { return instance; } -int subghz_protocol_encoder_get_rssi_threshold(void* context) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - - return instance->rssi_threshold; -} - void subghz_protocol_encoder_raw_stop(void* context) { SubGhzProtocolEncoderRAW* instance = context; instance->is_running = false; @@ -446,70 +342,6 @@ void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* } while(false); } -bool subghz_protocol_decoder_raw_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - if(instance->auto_mode) { - furi_assert(instance); - bool res = false; - FuriString* temp_str; - temp_str = furi_string_alloc(); - - do { - stream_clean(flipper_format_get_raw_stream(flipper_format)); - if(!flipper_format_write_header_cstr( - flipper_format, SUBGHZ_RAW_FILE_TYPE, SUBGHZ_RAW_FILE_VERSION)) { - FURI_LOG_E(TAG, "Unable to add header"); - break; - } - - if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { - FURI_LOG_E(TAG, "Unable to add Frequency"); - break; - } - subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); - if(!flipper_format_write_string_cstr( - flipper_format, "Preset", furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Unable to add Preset"); - break; - } - if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { - if(!flipper_format_write_string_cstr( - flipper_format, "Custom_preset_module", "CC1101")) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); - break; - } - if(!flipper_format_write_hex( - flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); - break; - } - } - if(!flipper_format_write_string_cstr( - flipper_format, "Protocol", instance->base.protocol->name)) { - FURI_LOG_E(TAG, "Unable to add Protocol"); - break; - } - - if(!flipper_format_write_int32( - flipper_format, "RAW_Data", instance->upload_raw, instance->ind_write)) { - FURI_LOG_E(TAG, "Unable to add Raw Data"); - break; - } else { - instance->ind_write = 0; - } - res = true; - } while(false); - furi_string_free(temp_str); - return res; - } else { - return false; - } -} - bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderRAW* instance = context; diff --git a/lib/subghz/protocols/raw.h b/lib/subghz/protocols/raw.h index 08efff50e..44c7faec5 100644 --- a/lib/subghz/protocols/raw.h +++ b/lib/subghz/protocols/raw.h @@ -2,8 +2,6 @@ #include "base.h" -#include - #define SUBGHZ_PROTOCOL_RAW_NAME "RAW" #ifdef __cplusplus @@ -31,27 +29,6 @@ bool subghz_protocol_raw_save_to_file_init( const char* dev_name, SubGhzRadioPreset* preset); -/** - * Set SubGhzProtocolDecoderRAW to auto mode, which allows subghz_scene_receiver to capture RAW. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param auto_mode Whether or not to enable auto mode - */ -void subghz_protocol_decoder_raw_set_auto_mode(void* context, bool auto_mode); - -/** - * Set RSSI threshold ("sensitivity" level). - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param rssi_threshold The desired RSSI threshold - */ -void subghz_protocol_decoder_raw_set_rssi_threshold(void* context, int rssi_threshold); - -/** - * Get RSSI threshold ("sensitivity" level). - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @return rssi threshold in db - */ -int subghz_protocol_encoder_get_rssi_threshold(void* context); - /** * Stop writing file to flash * @param instance Pointer to a SubGhzProtocolDecoderRAW instance @@ -84,15 +61,6 @@ void subghz_protocol_decoder_raw_free(void* context); */ void subghz_protocol_decoder_raw_reset(void* context); -/** - * Write raw data to the instance's internal buffer. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - * @return whether or not data was written - */ -bool subghz_protocol_decoder_raw_write_data(void* context, bool level, uint32_t duration); - /** * Parse a raw sequence of levels and durations received from the air. * @param context Pointer to a SubGhzProtocolDecoderRAW instance @@ -103,19 +71,12 @@ void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t durati /** * Deserialize data SubGhzProtocolDecoderRAW. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param flipper_format Pointer to a FlipperFormat instance + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format); -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_raw_get_hash_data(void* context); - /** * Getting a textual representation of the received data. * @param context Pointer to a SubGhzProtocolDecoderRAW instance @@ -167,19 +128,6 @@ void subghz_protocol_raw_file_encoder_worker_set_callback_end( */ void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path); -/** - * Serialize data SubGhzProtocolDecoderRAW. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset - * @return true On success - */ -bool subghz_protocol_decoder_raw_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - /** * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderRAW instance @@ -191,7 +139,7 @@ bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipp /** * Getting the level and duration of the upload to be loaded into DMA. * @param context Pointer to a SubGhzProtocolEncoderRAW instance - * @return LevelDuration + * @return LevelDuration */ LevelDuration subghz_protocol_encoder_raw_yield(void* context); diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 27a82beab..bcef90dad 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -261,16 +261,16 @@ static bool data = order << 4 | invert; int k = 0; for(int i = 6; i >= 0; i -= 2) { - roll_array[k++] = (data >> i) & 0x03; - if(roll_array[k] == 3) { + roll_array[k] = (data >> i) & 0x03; + if(roll_array[k++] == 3) { FURI_LOG_E(TAG, "Roll_Array FAIL"); return false; } } for(int i = 8; i >= 0; i -= 2) { - roll_array[k++] = (p[2] >> i) & 0x03; - if(roll_array[k] == 3) { + roll_array[k] = (p[2] >> i) & 0x03; + if(roll_array[k++] == 3) { FURI_LOG_E(TAG, "Roll_Array FAIL"); return false; } diff --git a/lib/subghz/receiver.c b/lib/subghz/receiver.c index d7b422170..698fe098e 100644 --- a/lib/subghz/receiver.c +++ b/lib/subghz/receiver.c @@ -108,11 +108,6 @@ void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag fil instance->filter = filter; } -SubGhzProtocolFlag subghz_receiver_get_filter(SubGhzReceiver* instance) { - furi_assert(instance); - return instance->filter; -} - SubGhzProtocolDecoderBase* subghz_receiver_search_decoder_base_by_name( SubGhzReceiver* instance, const char* decoder_name) { diff --git a/lib/subghz/receiver.h b/lib/subghz/receiver.h index 15fc455fd..2ef722d1f 100644 --- a/lib/subghz/receiver.h +++ b/lib/subghz/receiver.h @@ -59,13 +59,6 @@ void subghz_receiver_set_rx_callback( */ void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag filter); -/** - * Get the filter of receivers that will work at the moment. - * @param instance Pointer to a SubGhzReceiver instance - * @return filter Filter, SubGhzProtocolFlag - */ -SubGhzProtocolFlag subghz_receiver_get_filter(SubGhzReceiver* instance); - /** * Search for a cattery by his name. * @param instance Pointer to a SubGhzReceiver instance diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 42124bebc..e380fc967 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -70,7 +70,7 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* furi_delay_tick(1); } //waiting for reception to complete - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); @@ -104,14 +104,16 @@ void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t si furi_hal_subghz_write_packet(data, size); furi_hal_subghz_tx(); //start send instance->status = SubGhzTxRxWorkerStatusTx; - while(!furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted + while(!furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be set -> sync transmitted furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); break; } } - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet + while(furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be cleared -> end of packet furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); @@ -134,7 +136,7 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { furi_hal_subghz_idle(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_set_frequency_and_path(instance->frequency); furi_hal_subghz_flush_rx(); @@ -233,7 +235,7 @@ bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { instance->worker_running = true; - if(furi_hal_region_is_frequency_allowed(frequency)) { + if(furi_hal_subghz_is_tx_allowed(frequency)) { instance->frequency = frequency; res = true; } diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 1b8ef6a14..9d121dc3c 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -77,6 +77,7 @@ typedef enum { SubGhzProtocolTypeRAW, SubGhzProtocolWeatherStation, SubGhzProtocolCustom, + SubGhzProtocolTypeBinRAW, } SubGhzProtocolType; typedef enum { From 708dd167c8c57b453978d406448c6769fccb5435 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 10 Feb 2023 17:36:22 +0100 Subject: [PATCH 3/5] Merge branch 'dev' of https://github.com/ClaraCrazy/Flipper-Xtreme into dev --- .gitignore | 2 - .../main/archive/helpers/archive_browser.c | 12 +- .../main/archive/helpers/archive_browser.h | 2 - .../main/archive/views/archive_browser_view.c | 1 - .../main/archive/views/archive_browser_view.h | 1 - .../main/bad_usb/scenes/bad_usb_scene_error.c | 4 +- .../main/bad_usb/views/bad_usb_view.c | 9 +- .../main/u2f/scenes/u2f_scene_error.c | 4 +- applications/main/u2f/views/u2f_view.c | 10 +- .../plugins/nightstand/application.fam | 1 + applications/plugins/pomodoro/application.fam | 2 +- .../plugins/scrambler/application.fam | 2 +- .../desktop/animations/animation_manager.c | 20 +- .../desktop/animations/animation_storage.c | 10 +- applications/services/desktop/desktop.c | 5 - .../desktop/scenes/desktop_scene_fault.c | 4 +- .../settings/dolphin_passport/passport.c | 2 +- .../scenes/power_settings_scene_power_off.c | 2 +- .../scenes/xtreme_settings_scene_start.c | 14 + .../settings/xtreme_settings/xtreme_assets.c | 282 ++++++++++-------- .../settings/xtreme_settings/xtreme_assets.h | 13 +- .../xtreme_settings/xtreme_settings.h | 1 + .../NSFW/Icons/BLE/BLE_Pairing_128x64.png | Bin 2610 -> 0 bytes .../Icons/Dolphin/DolphinCommon_56x48.png | Bin 3376 -> 0 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 5390 -> 0 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 4224 -> 0 bytes .../NSFW/Icons/Passport/passport_DB.png | Bin 852 -> 0 bytes .../Icons/Passport/passport_happy_46x49.png | Bin 6373 -> 0 bytes .../Icons/Passport/passport_okay_46x49.png | Bin 6373 -> 0 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.png | Bin 4862 -> 0 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.png | Bin 4882 -> 0 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 4466 -> 0 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.png | Bin 3798 -> 0 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.png | Bin 4092 -> 0 bytes .../custom/NSFW/Icons/U2F/Auth_62x31.png | Bin 1864 -> 0 bytes .../NSFW/Icons/U2F/Connect_me_62x31.png | Bin 1895 -> 0 bytes .../custom/NSFW/Icons/U2F/Connected_62x31.png | Bin 1874 -> 0 bytes .../custom/NSFW/Icons/U2F/Error_62x31.png | Bin 1863 -> 0 bytes .../Icons/iButton/DolphinMafia_115x62.png | Bin 6876 -> 0 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.png | Bin 5422 -> 0 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.png | Bin 5122 -> 0 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 4719 -> 0 bytes .../nsfw}/PaxGod_TikTok_Marketing/frame_0.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_1.png | Bin .../PaxGod_TikTok_Marketing/frame_10.png | Bin .../PaxGod_TikTok_Marketing/frame_11.png | Bin .../PaxGod_TikTok_Marketing/frame_12.png | Bin .../PaxGod_TikTok_Marketing/frame_13.png | Bin .../PaxGod_TikTok_Marketing/frame_14.png | Bin .../PaxGod_TikTok_Marketing/frame_15.png | Bin .../PaxGod_TikTok_Marketing/frame_16.png | Bin .../PaxGod_TikTok_Marketing/frame_17.png | Bin .../PaxGod_TikTok_Marketing/frame_18.png | Bin .../PaxGod_TikTok_Marketing/frame_19.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_2.png | Bin .../PaxGod_TikTok_Marketing/frame_20.png | Bin .../PaxGod_TikTok_Marketing/frame_21.png | Bin .../PaxGod_TikTok_Marketing/frame_22.png | Bin .../PaxGod_TikTok_Marketing/frame_23.png | Bin .../PaxGod_TikTok_Marketing/frame_24.png | Bin .../PaxGod_TikTok_Marketing/frame_25.png | Bin .../PaxGod_TikTok_Marketing/frame_26.png | Bin .../PaxGod_TikTok_Marketing/frame_27.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_3.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_4.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_5.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_6.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_7.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_8.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_9.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/meta.txt | 0 .../Anims => external/nsfw}/lvl_1/frame_0.png | Bin .../Anims => external/nsfw}/lvl_1/frame_1.png | Bin .../nsfw}/lvl_1/frame_10.png | Bin .../nsfw}/lvl_1/frame_11.png | Bin .../nsfw}/lvl_1/frame_12.png | Bin .../nsfw}/lvl_1/frame_13.png | Bin .../nsfw}/lvl_1/frame_14.png | Bin .../nsfw}/lvl_1/frame_15.png | Bin .../nsfw}/lvl_1/frame_16.png | Bin .../nsfw}/lvl_1/frame_17.png | Bin .../nsfw}/lvl_1/frame_18.png | Bin .../nsfw}/lvl_1/frame_19.png | Bin .../Anims => external/nsfw}/lvl_1/frame_2.png | Bin .../nsfw}/lvl_1/frame_20.png | Bin .../nsfw}/lvl_1/frame_21.png | Bin .../nsfw}/lvl_1/frame_22.png | Bin .../nsfw}/lvl_1/frame_23.png | Bin .../nsfw}/lvl_1/frame_24.png | Bin .../nsfw}/lvl_1/frame_25.png | Bin .../nsfw}/lvl_1/frame_26.png | Bin .../nsfw}/lvl_1/frame_27.png | Bin .../nsfw}/lvl_1/frame_28.png | Bin .../nsfw}/lvl_1/frame_29.png | Bin .../Anims => external/nsfw}/lvl_1/frame_3.png | Bin .../nsfw}/lvl_1/frame_30.png | Bin .../Anims => external/nsfw}/lvl_1/frame_4.png | Bin .../Anims => external/nsfw}/lvl_1/frame_5.png | Bin .../Anims => external/nsfw}/lvl_1/frame_6.png | Bin .../Anims => external/nsfw}/lvl_1/frame_7.png | Bin .../Anims => external/nsfw}/lvl_1/frame_8.png | Bin .../Anims => external/nsfw}/lvl_1/frame_9.png | Bin .../Anims => external/nsfw}/lvl_1/meta.txt | 0 .../nsfw}/lvl_10/frame_0.png | Bin .../nsfw}/lvl_10/frame_1.png | Bin .../nsfw}/lvl_10/frame_10.png | Bin .../nsfw}/lvl_10/frame_11.png | Bin .../nsfw}/lvl_10/frame_12.png | Bin .../nsfw}/lvl_10/frame_13.png | Bin .../nsfw}/lvl_10/frame_14.png | Bin .../nsfw}/lvl_10/frame_15.png | Bin .../nsfw}/lvl_10/frame_16.png | Bin .../nsfw}/lvl_10/frame_17.png | Bin .../nsfw}/lvl_10/frame_18.png | Bin .../nsfw}/lvl_10/frame_19.png | Bin .../nsfw}/lvl_10/frame_2.png | Bin .../nsfw}/lvl_10/frame_20.png | Bin .../nsfw}/lvl_10/frame_21.png | Bin .../nsfw}/lvl_10/frame_22.png | Bin .../nsfw}/lvl_10/frame_23.png | Bin .../nsfw}/lvl_10/frame_24.png | Bin .../nsfw}/lvl_10/frame_25.png | Bin .../nsfw}/lvl_10/frame_26.png | Bin .../nsfw}/lvl_10/frame_27.png | Bin .../nsfw}/lvl_10/frame_3.png | Bin .../nsfw}/lvl_10/frame_4.png | Bin .../nsfw}/lvl_10/frame_5.png | Bin .../nsfw}/lvl_10/frame_6.png | Bin .../nsfw}/lvl_10/frame_7.png | Bin .../nsfw}/lvl_10/frame_8.png | Bin .../nsfw}/lvl_10/frame_9.png | Bin .../Anims => external/nsfw}/lvl_10/meta.txt | 0 .../nsfw}/lvl_11/frame_0.png | Bin .../nsfw}/lvl_11/frame_1.png | Bin .../nsfw}/lvl_11/frame_10.png | Bin .../nsfw}/lvl_11/frame_11.png | Bin .../nsfw}/lvl_11/frame_12.png | Bin .../nsfw}/lvl_11/frame_13.png | Bin .../nsfw}/lvl_11/frame_14.png | Bin .../nsfw}/lvl_11/frame_15.png | Bin .../nsfw}/lvl_11/frame_16.png | Bin .../nsfw}/lvl_11/frame_17.png | Bin .../nsfw}/lvl_11/frame_18.png | Bin .../nsfw}/lvl_11/frame_19.png | Bin .../nsfw}/lvl_11/frame_2.png | Bin .../nsfw}/lvl_11/frame_20.png | Bin .../nsfw}/lvl_11/frame_21.png | Bin .../nsfw}/lvl_11/frame_22.png | Bin .../nsfw}/lvl_11/frame_23.png | Bin .../nsfw}/lvl_11/frame_24.png | Bin .../nsfw}/lvl_11/frame_25.png | Bin .../nsfw}/lvl_11/frame_26.png | Bin .../nsfw}/lvl_11/frame_27.png | Bin .../nsfw}/lvl_11/frame_28.png | Bin .../nsfw}/lvl_11/frame_29.png | Bin .../nsfw}/lvl_11/frame_3.png | Bin .../nsfw}/lvl_11/frame_30.png | Bin .../nsfw}/lvl_11/frame_31.png | Bin .../nsfw}/lvl_11/frame_32.png | Bin .../nsfw}/lvl_11/frame_33.png | Bin .../nsfw}/lvl_11/frame_34.png | Bin .../nsfw}/lvl_11/frame_35.png | Bin .../nsfw}/lvl_11/frame_36.png | Bin .../nsfw}/lvl_11/frame_37.png | Bin .../nsfw}/lvl_11/frame_38.png | Bin .../nsfw}/lvl_11/frame_39.png | Bin .../nsfw}/lvl_11/frame_4.png | Bin .../nsfw}/lvl_11/frame_40.png | Bin .../nsfw}/lvl_11/frame_41.png | Bin .../nsfw}/lvl_11/frame_42.png | Bin .../nsfw}/lvl_11/frame_43.png | Bin .../nsfw}/lvl_11/frame_44.png | Bin .../nsfw}/lvl_11/frame_45.png | Bin .../nsfw}/lvl_11/frame_46.png | Bin .../nsfw}/lvl_11/frame_47.png | Bin .../nsfw}/lvl_11/frame_48.png | Bin .../nsfw}/lvl_11/frame_49.png | Bin .../nsfw}/lvl_11/frame_5.png | Bin .../nsfw}/lvl_11/frame_6.png | Bin .../nsfw}/lvl_11/frame_7.png | Bin .../nsfw}/lvl_11/frame_8.png | Bin .../nsfw}/lvl_11/frame_9.png | Bin .../Anims => external/nsfw}/lvl_11/meta.txt | 0 .../nsfw}/lvl_12/frame_0.png | Bin .../nsfw}/lvl_12/frame_1.png | Bin .../nsfw}/lvl_12/frame_10.png | Bin .../nsfw}/lvl_12/frame_11.png | Bin .../nsfw}/lvl_12/frame_12.png | Bin .../nsfw}/lvl_12/frame_13.png | Bin .../nsfw}/lvl_12/frame_14.png | Bin .../nsfw}/lvl_12/frame_15.png | Bin .../nsfw}/lvl_12/frame_2.png | Bin .../nsfw}/lvl_12/frame_3.png | Bin .../nsfw}/lvl_12/frame_4.png | Bin .../nsfw}/lvl_12/frame_5.png | Bin .../nsfw}/lvl_12/frame_6.png | Bin .../nsfw}/lvl_12/frame_7.png | Bin .../nsfw}/lvl_12/frame_8.png | Bin .../nsfw}/lvl_12/frame_9.png | Bin .../Anims => external/nsfw}/lvl_12/meta.txt | 0 .../nsfw}/lvl_13/frame_0.png | Bin .../nsfw}/lvl_13/frame_1.png | Bin .../nsfw}/lvl_13/frame_10.png | Bin .../nsfw}/lvl_13/frame_2.png | Bin .../nsfw}/lvl_13/frame_3.png | Bin .../nsfw}/lvl_13/frame_4.png | Bin .../nsfw}/lvl_13/frame_5.png | Bin .../nsfw}/lvl_13/frame_6.png | Bin .../nsfw}/lvl_13/frame_7.png | Bin .../nsfw}/lvl_13/frame_8.png | Bin .../nsfw}/lvl_13/frame_9.png | Bin .../Anims => external/nsfw}/lvl_13/meta.txt | 0 .../nsfw}/lvl_14/frame_0.png | Bin .../nsfw}/lvl_14/frame_1.png | Bin .../nsfw}/lvl_14/frame_2.png | Bin .../nsfw}/lvl_14/frame_3.png | Bin .../nsfw}/lvl_14/frame_4.png | Bin .../nsfw}/lvl_14/frame_5.png | Bin .../nsfw}/lvl_14/frame_6.png | Bin .../nsfw}/lvl_14/frame_7.png | Bin .../Anims => external/nsfw}/lvl_14/meta.txt | 0 .../nsfw}/lvl_15/frame_0.png | Bin .../nsfw}/lvl_15/frame_1.png | Bin .../nsfw}/lvl_15/frame_10.png | Bin .../nsfw}/lvl_15/frame_11.png | Bin .../nsfw}/lvl_15/frame_12.png | Bin .../nsfw}/lvl_15/frame_13.png | Bin .../nsfw}/lvl_15/frame_14.png | Bin .../nsfw}/lvl_15/frame_15.png | Bin .../nsfw}/lvl_15/frame_16.png | Bin .../nsfw}/lvl_15/frame_17.png | Bin .../nsfw}/lvl_15/frame_18.png | Bin .../nsfw}/lvl_15/frame_19.png | Bin .../nsfw}/lvl_15/frame_2.png | Bin .../nsfw}/lvl_15/frame_20.png | Bin .../nsfw}/lvl_15/frame_21.png | Bin .../nsfw}/lvl_15/frame_3.png | Bin .../nsfw}/lvl_15/frame_4.png | Bin .../nsfw}/lvl_15/frame_5.png | Bin .../nsfw}/lvl_15/frame_6.png | Bin .../nsfw}/lvl_15/frame_7.png | Bin .../nsfw}/lvl_15/frame_8.png | Bin .../nsfw}/lvl_15/frame_9.png | Bin .../Anims => external/nsfw}/lvl_15/meta.txt | 0 .../nsfw}/lvl_16/frame_0.png | Bin .../nsfw}/lvl_16/frame_1.png | Bin .../nsfw}/lvl_16/frame_10.png | Bin .../nsfw}/lvl_16/frame_11.png | Bin .../nsfw}/lvl_16/frame_12.png | Bin .../nsfw}/lvl_16/frame_13.png | Bin .../nsfw}/lvl_16/frame_14.png | Bin .../nsfw}/lvl_16/frame_15.png | Bin .../nsfw}/lvl_16/frame_16.png | Bin .../nsfw}/lvl_16/frame_17.png | Bin .../nsfw}/lvl_16/frame_18.png | Bin .../nsfw}/lvl_16/frame_19.png | Bin .../nsfw}/lvl_16/frame_2.png | Bin .../nsfw}/lvl_16/frame_20.png | Bin .../nsfw}/lvl_16/frame_3.png | Bin .../nsfw}/lvl_16/frame_4.png | Bin .../nsfw}/lvl_16/frame_5.png | Bin .../nsfw}/lvl_16/frame_6.png | Bin .../nsfw}/lvl_16/frame_7.png | Bin .../nsfw}/lvl_16/frame_8.png | Bin .../nsfw}/lvl_16/frame_9.png | Bin .../Anims => external/nsfw}/lvl_16/meta.txt | 0 .../nsfw}/lvl_17/frame_0.png | Bin .../nsfw}/lvl_17/frame_1.png | Bin .../nsfw}/lvl_17/frame_10.png | Bin .../nsfw}/lvl_17/frame_11.png | Bin .../nsfw}/lvl_17/frame_12.png | Bin .../nsfw}/lvl_17/frame_13.png | Bin .../nsfw}/lvl_17/frame_14.png | Bin .../nsfw}/lvl_17/frame_15.png | Bin .../nsfw}/lvl_17/frame_16.png | Bin .../nsfw}/lvl_17/frame_17.png | Bin .../nsfw}/lvl_17/frame_18.png | Bin .../nsfw}/lvl_17/frame_19.png | Bin .../nsfw}/lvl_17/frame_2.png | Bin .../nsfw}/lvl_17/frame_20.png | Bin .../nsfw}/lvl_17/frame_21.png | Bin .../nsfw}/lvl_17/frame_22.png | Bin .../nsfw}/lvl_17/frame_23.png | Bin .../nsfw}/lvl_17/frame_24.png | Bin .../nsfw}/lvl_17/frame_25.png | Bin .../nsfw}/lvl_17/frame_26.png | Bin .../nsfw}/lvl_17/frame_27.png | Bin .../nsfw}/lvl_17/frame_28.png | Bin .../nsfw}/lvl_17/frame_29.png | Bin .../nsfw}/lvl_17/frame_3.png | Bin .../nsfw}/lvl_17/frame_30.png | Bin .../nsfw}/lvl_17/frame_31.png | Bin .../nsfw}/lvl_17/frame_4.png | Bin .../nsfw}/lvl_17/frame_5.png | Bin .../nsfw}/lvl_17/frame_6.png | Bin .../nsfw}/lvl_17/frame_7.png | Bin .../nsfw}/lvl_17/frame_8.png | Bin .../nsfw}/lvl_17/frame_9.png | Bin .../Anims => external/nsfw}/lvl_17/meta.txt | 0 .../nsfw}/lvl_18/frame_0.png | Bin .../nsfw}/lvl_18/frame_1.png | Bin .../nsfw}/lvl_18/frame_10.png | Bin .../nsfw}/lvl_18/frame_11.png | Bin .../nsfw}/lvl_18/frame_12.png | Bin .../nsfw}/lvl_18/frame_13.png | Bin .../nsfw}/lvl_18/frame_14.png | Bin .../nsfw}/lvl_18/frame_15.png | Bin .../nsfw}/lvl_18/frame_16.png | Bin .../nsfw}/lvl_18/frame_17.png | Bin .../nsfw}/lvl_18/frame_18.png | Bin .../nsfw}/lvl_18/frame_19.png | Bin .../nsfw}/lvl_18/frame_2.png | Bin .../nsfw}/lvl_18/frame_20.png | Bin .../nsfw}/lvl_18/frame_21.png | Bin .../nsfw}/lvl_18/frame_22.png | Bin .../nsfw}/lvl_18/frame_3.png | Bin .../nsfw}/lvl_18/frame_4.png | Bin .../nsfw}/lvl_18/frame_5.png | Bin .../nsfw}/lvl_18/frame_6.png | Bin .../nsfw}/lvl_18/frame_7.png | Bin .../nsfw}/lvl_18/frame_8.png | Bin .../nsfw}/lvl_18/frame_9.png | Bin .../Anims => external/nsfw}/lvl_18/meta.txt | 0 .../nsfw}/lvl_19/frame_0.png | Bin .../nsfw}/lvl_19/frame_1.png | Bin .../nsfw}/lvl_19/frame_10.png | Bin .../nsfw}/lvl_19/frame_11.png | Bin .../nsfw}/lvl_19/frame_12.png | Bin .../nsfw}/lvl_19/frame_13.png | Bin .../nsfw}/lvl_19/frame_14.png | Bin .../nsfw}/lvl_19/frame_15.png | Bin .../nsfw}/lvl_19/frame_16.png | Bin .../nsfw}/lvl_19/frame_17.png | Bin .../nsfw}/lvl_19/frame_18.png | Bin .../nsfw}/lvl_19/frame_19.png | Bin .../nsfw}/lvl_19/frame_2.png | Bin .../nsfw}/lvl_19/frame_20.png | Bin .../nsfw}/lvl_19/frame_21.png | Bin .../nsfw}/lvl_19/frame_3.png | Bin .../nsfw}/lvl_19/frame_4.png | Bin .../nsfw}/lvl_19/frame_5.png | Bin .../nsfw}/lvl_19/frame_6.png | Bin .../nsfw}/lvl_19/frame_7.png | Bin .../nsfw}/lvl_19/frame_8.png | Bin .../nsfw}/lvl_19/frame_9.png | Bin .../Anims => external/nsfw}/lvl_19/meta.txt | 0 .../Anims => external/nsfw}/lvl_2/frame_0.png | Bin .../Anims => external/nsfw}/lvl_2/frame_1.png | Bin .../nsfw}/lvl_2/frame_10.png | Bin .../nsfw}/lvl_2/frame_11.png | Bin .../nsfw}/lvl_2/frame_12.png | Bin .../nsfw}/lvl_2/frame_13.png | Bin .../nsfw}/lvl_2/frame_14.png | Bin .../Anims => external/nsfw}/lvl_2/frame_2.png | Bin .../Anims => external/nsfw}/lvl_2/frame_3.png | Bin .../Anims => external/nsfw}/lvl_2/frame_4.png | Bin .../Anims => external/nsfw}/lvl_2/frame_5.png | Bin .../Anims => external/nsfw}/lvl_2/frame_6.png | Bin .../Anims => external/nsfw}/lvl_2/frame_7.png | Bin .../Anims => external/nsfw}/lvl_2/frame_8.png | Bin .../Anims => external/nsfw}/lvl_2/frame_9.png | Bin .../Anims => external/nsfw}/lvl_2/meta.txt | 0 .../nsfw}/lvl_20/frame_0.png | Bin .../nsfw}/lvl_20/frame_1.png | Bin .../nsfw}/lvl_20/frame_10.png | Bin .../nsfw}/lvl_20/frame_11.png | Bin .../nsfw}/lvl_20/frame_12.png | Bin .../nsfw}/lvl_20/frame_13.png | Bin .../nsfw}/lvl_20/frame_2.png | Bin .../nsfw}/lvl_20/frame_3.png | Bin .../nsfw}/lvl_20/frame_4.png | Bin .../nsfw}/lvl_20/frame_5.png | Bin .../nsfw}/lvl_20/frame_6.png | Bin .../nsfw}/lvl_20/frame_7.png | Bin .../nsfw}/lvl_20/frame_8.png | Bin .../nsfw}/lvl_20/frame_9.png | Bin .../Anims => external/nsfw}/lvl_20/meta.txt | 0 .../nsfw}/lvl_21/frame_0.png | Bin .../nsfw}/lvl_21/frame_1.png | Bin .../nsfw}/lvl_21/frame_2.png | Bin .../nsfw}/lvl_21/frame_3.png | Bin .../nsfw}/lvl_21/frame_4.png | Bin .../nsfw}/lvl_21/frame_5.png | Bin .../Anims => external/nsfw}/lvl_21/meta.txt | 0 .../nsfw}/lvl_22/frame_0.png | Bin .../nsfw}/lvl_22/frame_1.png | Bin .../nsfw}/lvl_22/frame_10.png | Bin .../nsfw}/lvl_22/frame_11.png | Bin .../nsfw}/lvl_22/frame_12.png | Bin .../nsfw}/lvl_22/frame_13.png | Bin .../nsfw}/lvl_22/frame_14.png | Bin .../nsfw}/lvl_22/frame_15.png | Bin .../nsfw}/lvl_22/frame_16.png | Bin .../nsfw}/lvl_22/frame_17.png | Bin .../nsfw}/lvl_22/frame_18.png | Bin .../nsfw}/lvl_22/frame_19.png | Bin .../nsfw}/lvl_22/frame_2.png | Bin .../nsfw}/lvl_22/frame_20.png | Bin .../nsfw}/lvl_22/frame_21.png | Bin .../nsfw}/lvl_22/frame_22.png | Bin .../nsfw}/lvl_22/frame_23.png | Bin .../nsfw}/lvl_22/frame_24.png | Bin .../nsfw}/lvl_22/frame_25.png | Bin .../nsfw}/lvl_22/frame_26.png | Bin .../nsfw}/lvl_22/frame_27.png | Bin .../nsfw}/lvl_22/frame_28.png | Bin .../nsfw}/lvl_22/frame_29.png | Bin .../nsfw}/lvl_22/frame_3.png | Bin .../nsfw}/lvl_22/frame_30.png | Bin .../nsfw}/lvl_22/frame_31.png | Bin .../nsfw}/lvl_22/frame_32.png | Bin .../nsfw}/lvl_22/frame_33.png | Bin .../nsfw}/lvl_22/frame_34.png | Bin .../nsfw}/lvl_22/frame_35.png | Bin .../nsfw}/lvl_22/frame_36.png | Bin .../nsfw}/lvl_22/frame_37.png | Bin .../nsfw}/lvl_22/frame_38.png | Bin .../nsfw}/lvl_22/frame_39.png | Bin .../nsfw}/lvl_22/frame_4.png | Bin .../nsfw}/lvl_22/frame_40.png | Bin .../nsfw}/lvl_22/frame_41.png | Bin .../nsfw}/lvl_22/frame_42.png | Bin .../nsfw}/lvl_22/frame_43.png | Bin .../nsfw}/lvl_22/frame_44.png | Bin .../nsfw}/lvl_22/frame_45.png | Bin .../nsfw}/lvl_22/frame_46.png | Bin .../nsfw}/lvl_22/frame_47.png | Bin .../nsfw}/lvl_22/frame_48.png | Bin .../nsfw}/lvl_22/frame_49.png | Bin .../nsfw}/lvl_22/frame_5.png | Bin .../nsfw}/lvl_22/frame_50.png | Bin .../nsfw}/lvl_22/frame_51.png | Bin .../nsfw}/lvl_22/frame_52.png | Bin .../nsfw}/lvl_22/frame_53.png | Bin .../nsfw}/lvl_22/frame_54.png | Bin .../nsfw}/lvl_22/frame_55.png | Bin .../nsfw}/lvl_22/frame_56.png | Bin .../nsfw}/lvl_22/frame_57.png | Bin .../nsfw}/lvl_22/frame_58.png | Bin .../nsfw}/lvl_22/frame_59.png | Bin .../nsfw}/lvl_22/frame_6.png | Bin .../nsfw}/lvl_22/frame_7.png | Bin .../nsfw}/lvl_22/frame_8.png | Bin .../nsfw}/lvl_22/frame_9.png | Bin .../Anims => external/nsfw}/lvl_22/meta.txt | 0 .../nsfw}/lvl_23/frame_0.png | Bin .../nsfw}/lvl_23/frame_1.png | Bin .../nsfw}/lvl_23/frame_10.png | Bin .../nsfw}/lvl_23/frame_11.png | Bin .../nsfw}/lvl_23/frame_12.png | Bin .../nsfw}/lvl_23/frame_13.png | Bin .../nsfw}/lvl_23/frame_14.png | Bin .../nsfw}/lvl_23/frame_15.png | Bin .../nsfw}/lvl_23/frame_16.png | Bin .../nsfw}/lvl_23/frame_2.png | Bin .../nsfw}/lvl_23/frame_3.png | Bin .../nsfw}/lvl_23/frame_4.png | Bin .../nsfw}/lvl_23/frame_5.png | Bin .../nsfw}/lvl_23/frame_6.png | Bin .../nsfw}/lvl_23/frame_7.png | Bin .../nsfw}/lvl_23/frame_8.png | Bin .../nsfw}/lvl_23/frame_9.png | Bin .../Anims => external/nsfw}/lvl_23/meta.txt | 0 .../nsfw}/lvl_24/frame_0.png | Bin .../nsfw}/lvl_24/frame_1.png | Bin .../nsfw}/lvl_24/frame_10.png | Bin .../nsfw}/lvl_24/frame_11.png | Bin .../nsfw}/lvl_24/frame_12.png | Bin .../nsfw}/lvl_24/frame_13.png | Bin .../nsfw}/lvl_24/frame_14.png | Bin .../nsfw}/lvl_24/frame_15.png | Bin .../nsfw}/lvl_24/frame_16.png | Bin .../nsfw}/lvl_24/frame_17.png | Bin .../nsfw}/lvl_24/frame_18.png | Bin .../nsfw}/lvl_24/frame_19.png | Bin .../nsfw}/lvl_24/frame_2.png | Bin .../nsfw}/lvl_24/frame_20.png | Bin .../nsfw}/lvl_24/frame_21.png | Bin .../nsfw}/lvl_24/frame_22.png | Bin .../nsfw}/lvl_24/frame_23.png | Bin .../nsfw}/lvl_24/frame_24.png | Bin .../nsfw}/lvl_24/frame_25.png | Bin .../nsfw}/lvl_24/frame_26.png | Bin .../nsfw}/lvl_24/frame_27.png | Bin .../nsfw}/lvl_24/frame_28.png | Bin .../nsfw}/lvl_24/frame_29.png | Bin .../nsfw}/lvl_24/frame_3.png | Bin .../nsfw}/lvl_24/frame_4.png | Bin .../nsfw}/lvl_24/frame_5.png | Bin .../nsfw}/lvl_24/frame_6.png | Bin .../nsfw}/lvl_24/frame_7.png | Bin .../nsfw}/lvl_24/frame_8.png | Bin .../nsfw}/lvl_24/frame_9.png | Bin .../Anims => external/nsfw}/lvl_24/meta.txt | 0 .../nsfw}/lvl_25/frame_0.png | Bin .../nsfw}/lvl_25/frame_1.png | Bin .../nsfw}/lvl_25/frame_10.png | Bin .../nsfw}/lvl_25/frame_11.png | Bin .../nsfw}/lvl_25/frame_12.png | Bin .../nsfw}/lvl_25/frame_13.png | Bin .../nsfw}/lvl_25/frame_14.png | Bin .../nsfw}/lvl_25/frame_15.png | Bin .../nsfw}/lvl_25/frame_16.png | Bin .../nsfw}/lvl_25/frame_17.png | Bin .../nsfw}/lvl_25/frame_18.png | Bin .../nsfw}/lvl_25/frame_19.png | Bin .../nsfw}/lvl_25/frame_2.png | Bin .../nsfw}/lvl_25/frame_20.png | Bin .../nsfw}/lvl_25/frame_21.png | Bin .../nsfw}/lvl_25/frame_22.png | Bin .../nsfw}/lvl_25/frame_23.png | Bin .../nsfw}/lvl_25/frame_24.png | Bin .../nsfw}/lvl_25/frame_25.png | Bin .../nsfw}/lvl_25/frame_26.png | Bin .../nsfw}/lvl_25/frame_27.png | Bin .../nsfw}/lvl_25/frame_28.png | Bin .../nsfw}/lvl_25/frame_29.png | Bin .../nsfw}/lvl_25/frame_3.png | Bin .../nsfw}/lvl_25/frame_30.png | Bin .../nsfw}/lvl_25/frame_31.png | Bin .../nsfw}/lvl_25/frame_32.png | Bin .../nsfw}/lvl_25/frame_33.png | Bin .../nsfw}/lvl_25/frame_34.png | Bin .../nsfw}/lvl_25/frame_35.png | Bin .../nsfw}/lvl_25/frame_4.png | Bin .../nsfw}/lvl_25/frame_5.png | Bin .../nsfw}/lvl_25/frame_6.png | Bin .../nsfw}/lvl_25/frame_7.png | Bin .../nsfw}/lvl_25/frame_8.png | Bin .../nsfw}/lvl_25/frame_9.png | Bin .../Anims => external/nsfw}/lvl_25/meta.txt | 0 .../nsfw}/lvl_26/frame_0.png | Bin .../nsfw}/lvl_26/frame_1.png | Bin .../nsfw}/lvl_26/frame_10.png | Bin .../nsfw}/lvl_26/frame_11.png | Bin .../nsfw}/lvl_26/frame_2.png | Bin .../nsfw}/lvl_26/frame_3.png | Bin .../nsfw}/lvl_26/frame_4.png | Bin .../nsfw}/lvl_26/frame_5.png | Bin .../nsfw}/lvl_26/frame_6.png | Bin .../nsfw}/lvl_26/frame_7.png | Bin .../nsfw}/lvl_26/frame_8.png | Bin .../nsfw}/lvl_26/frame_9.png | Bin .../Anims => external/nsfw}/lvl_26/meta.txt | 0 .../nsfw}/lvl_27/frame_0.png | Bin .../nsfw}/lvl_27/frame_1.png | Bin .../nsfw}/lvl_27/frame_10.png | Bin .../nsfw}/lvl_27/frame_11.png | Bin .../nsfw}/lvl_27/frame_12.png | Bin .../nsfw}/lvl_27/frame_13.png | Bin .../nsfw}/lvl_27/frame_14.png | Bin .../nsfw}/lvl_27/frame_15.png | Bin .../nsfw}/lvl_27/frame_16.png | Bin .../nsfw}/lvl_27/frame_17.png | Bin .../nsfw}/lvl_27/frame_18.png | Bin .../nsfw}/lvl_27/frame_19.png | Bin .../nsfw}/lvl_27/frame_2.png | Bin .../nsfw}/lvl_27/frame_20.png | Bin .../nsfw}/lvl_27/frame_21.png | Bin .../nsfw}/lvl_27/frame_3.png | Bin .../nsfw}/lvl_27/frame_4.png | Bin .../nsfw}/lvl_27/frame_5.png | Bin .../nsfw}/lvl_27/frame_6.png | Bin .../nsfw}/lvl_27/frame_7.png | Bin .../nsfw}/lvl_27/frame_8.png | Bin .../nsfw}/lvl_27/frame_9.png | Bin .../Anims => external/nsfw}/lvl_27/meta.txt | 0 .../nsfw}/lvl_28/frame_0.png | Bin .../nsfw}/lvl_28/frame_1.png | Bin .../nsfw}/lvl_28/frame_2.png | Bin .../nsfw}/lvl_28/frame_3.png | Bin .../nsfw}/lvl_28/frame_4.png | Bin .../nsfw}/lvl_28/frame_5.png | Bin .../Anims => external/nsfw}/lvl_28/meta.txt | 0 .../nsfw}/lvl_29/frame_0.png | Bin .../nsfw}/lvl_29/frame_1.png | Bin .../nsfw}/lvl_29/frame_10.png | Bin .../nsfw}/lvl_29/frame_11.png | Bin .../nsfw}/lvl_29/frame_12.png | Bin .../nsfw}/lvl_29/frame_13.png | Bin .../nsfw}/lvl_29/frame_14.png | Bin .../nsfw}/lvl_29/frame_15.png | Bin .../nsfw}/lvl_29/frame_16.png | Bin .../nsfw}/lvl_29/frame_17.png | Bin .../nsfw}/lvl_29/frame_18.png | Bin .../nsfw}/lvl_29/frame_19.png | Bin .../nsfw}/lvl_29/frame_2.png | Bin .../nsfw}/lvl_29/frame_20.png | Bin .../nsfw}/lvl_29/frame_21.png | Bin .../nsfw}/lvl_29/frame_22.png | Bin .../nsfw}/lvl_29/frame_23.png | Bin .../nsfw}/lvl_29/frame_24.png | Bin .../nsfw}/lvl_29/frame_25.png | Bin .../nsfw}/lvl_29/frame_26.png | Bin .../nsfw}/lvl_29/frame_27.png | Bin .../nsfw}/lvl_29/frame_28.png | Bin .../nsfw}/lvl_29/frame_29.png | Bin .../nsfw}/lvl_29/frame_3.png | Bin .../nsfw}/lvl_29/frame_30.png | Bin .../nsfw}/lvl_29/frame_31.png | Bin .../nsfw}/lvl_29/frame_32.png | Bin .../nsfw}/lvl_29/frame_33.png | Bin .../nsfw}/lvl_29/frame_34.png | Bin .../nsfw}/lvl_29/frame_35.png | Bin .../nsfw}/lvl_29/frame_36.png | Bin .../nsfw}/lvl_29/frame_37.png | Bin .../nsfw}/lvl_29/frame_38.png | Bin .../nsfw}/lvl_29/frame_39.png | Bin .../nsfw}/lvl_29/frame_4.png | Bin .../nsfw}/lvl_29/frame_40.png | Bin .../nsfw}/lvl_29/frame_41.png | Bin .../nsfw}/lvl_29/frame_42.png | Bin .../nsfw}/lvl_29/frame_43.png | Bin .../nsfw}/lvl_29/frame_44.png | Bin .../nsfw}/lvl_29/frame_45.png | Bin .../nsfw}/lvl_29/frame_46.png | Bin .../nsfw}/lvl_29/frame_47.png | Bin .../nsfw}/lvl_29/frame_48.png | Bin .../nsfw}/lvl_29/frame_49.png | Bin .../nsfw}/lvl_29/frame_5.png | Bin .../nsfw}/lvl_29/frame_50.png | Bin .../nsfw}/lvl_29/frame_51.png | Bin .../nsfw}/lvl_29/frame_6.png | Bin .../nsfw}/lvl_29/frame_7.png | Bin .../nsfw}/lvl_29/frame_8.png | Bin .../nsfw}/lvl_29/frame_9.png | Bin .../Anims => external/nsfw}/lvl_29/meta.txt | 0 .../Anims => external/nsfw}/lvl_3/frame_0.png | Bin .../Anims => external/nsfw}/lvl_3/frame_1.png | Bin .../nsfw}/lvl_3/frame_10.png | Bin .../nsfw}/lvl_3/frame_11.png | Bin .../nsfw}/lvl_3/frame_12.png | Bin .../nsfw}/lvl_3/frame_13.png | Bin .../nsfw}/lvl_3/frame_14.png | Bin .../Anims => external/nsfw}/lvl_3/frame_2.png | Bin .../Anims => external/nsfw}/lvl_3/frame_3.png | Bin .../Anims => external/nsfw}/lvl_3/frame_4.png | Bin .../Anims => external/nsfw}/lvl_3/frame_5.png | Bin .../Anims => external/nsfw}/lvl_3/frame_6.png | Bin .../Anims => external/nsfw}/lvl_3/frame_7.png | Bin .../Anims => external/nsfw}/lvl_3/frame_8.png | Bin .../Anims => external/nsfw}/lvl_3/frame_9.png | Bin .../Anims => external/nsfw}/lvl_3/meta.txt | 0 .../nsfw}/lvl_30/frame_0.png | Bin .../nsfw}/lvl_30/frame_1.png | Bin .../nsfw}/lvl_30/frame_10.png | Bin .../nsfw}/lvl_30/frame_11.png | Bin .../nsfw}/lvl_30/frame_12.png | Bin .../nsfw}/lvl_30/frame_13.png | Bin .../nsfw}/lvl_30/frame_14.png | Bin .../nsfw}/lvl_30/frame_15.png | Bin .../nsfw}/lvl_30/frame_16.png | Bin .../nsfw}/lvl_30/frame_17.png | Bin .../nsfw}/lvl_30/frame_18.png | Bin .../nsfw}/lvl_30/frame_19.png | Bin .../nsfw}/lvl_30/frame_2.png | Bin .../nsfw}/lvl_30/frame_20.png | Bin .../nsfw}/lvl_30/frame_21.png | Bin .../nsfw}/lvl_30/frame_22.png | Bin .../nsfw}/lvl_30/frame_23.png | Bin .../nsfw}/lvl_30/frame_24.png | Bin .../nsfw}/lvl_30/frame_25.png | Bin .../nsfw}/lvl_30/frame_26.png | Bin .../nsfw}/lvl_30/frame_27.png | Bin .../nsfw}/lvl_30/frame_28.png | Bin .../nsfw}/lvl_30/frame_29.png | Bin .../nsfw}/lvl_30/frame_3.png | Bin .../nsfw}/lvl_30/frame_30.png | Bin .../nsfw}/lvl_30/frame_31.png | Bin .../nsfw}/lvl_30/frame_32.png | Bin .../nsfw}/lvl_30/frame_33.png | Bin .../nsfw}/lvl_30/frame_34.png | Bin .../nsfw}/lvl_30/frame_35.png | Bin .../nsfw}/lvl_30/frame_36.png | Bin .../nsfw}/lvl_30/frame_37.png | Bin .../nsfw}/lvl_30/frame_38.png | Bin .../nsfw}/lvl_30/frame_39.png | Bin .../nsfw}/lvl_30/frame_4.png | Bin .../nsfw}/lvl_30/frame_40.png | Bin .../nsfw}/lvl_30/frame_41.png | Bin .../nsfw}/lvl_30/frame_42.png | Bin .../nsfw}/lvl_30/frame_43.png | Bin .../nsfw}/lvl_30/frame_44.png | Bin .../nsfw}/lvl_30/frame_45.png | Bin .../nsfw}/lvl_30/frame_46.png | Bin .../nsfw}/lvl_30/frame_47.png | Bin .../nsfw}/lvl_30/frame_48.png | Bin .../nsfw}/lvl_30/frame_49.png | Bin .../nsfw}/lvl_30/frame_5.png | Bin .../nsfw}/lvl_30/frame_6.png | Bin .../nsfw}/lvl_30/frame_7.png | Bin .../nsfw}/lvl_30/frame_8.png | Bin .../nsfw}/lvl_30/frame_9.png | Bin .../Anims => external/nsfw}/lvl_30/meta.txt | 0 .../Anims => external/nsfw}/lvl_4/frame_0.png | Bin .../Anims => external/nsfw}/lvl_4/frame_1.png | Bin .../nsfw}/lvl_4/frame_10.png | Bin .../nsfw}/lvl_4/frame_11.png | Bin .../nsfw}/lvl_4/frame_12.png | Bin .../nsfw}/lvl_4/frame_13.png | Bin .../nsfw}/lvl_4/frame_14.png | Bin .../nsfw}/lvl_4/frame_15.png | Bin .../nsfw}/lvl_4/frame_16.png | Bin .../nsfw}/lvl_4/frame_17.png | Bin .../nsfw}/lvl_4/frame_18.png | Bin .../nsfw}/lvl_4/frame_19.png | Bin .../Anims => external/nsfw}/lvl_4/frame_2.png | Bin .../Anims => external/nsfw}/lvl_4/frame_3.png | Bin .../Anims => external/nsfw}/lvl_4/frame_4.png | Bin .../Anims => external/nsfw}/lvl_4/frame_5.png | Bin .../Anims => external/nsfw}/lvl_4/frame_6.png | Bin .../Anims => external/nsfw}/lvl_4/frame_7.png | Bin .../Anims => external/nsfw}/lvl_4/frame_8.png | Bin .../Anims => external/nsfw}/lvl_4/frame_9.png | Bin .../Anims => external/nsfw}/lvl_4/meta.txt | 0 .../Anims => external/nsfw}/lvl_5/frame_0.png | Bin .../Anims => external/nsfw}/lvl_5/frame_1.png | Bin .../nsfw}/lvl_5/frame_10.png | Bin .../nsfw}/lvl_5/frame_11.png | Bin .../nsfw}/lvl_5/frame_12.png | Bin .../nsfw}/lvl_5/frame_13.png | Bin .../nsfw}/lvl_5/frame_14.png | Bin .../nsfw}/lvl_5/frame_15.png | Bin .../nsfw}/lvl_5/frame_16.png | Bin .../nsfw}/lvl_5/frame_17.png | Bin .../nsfw}/lvl_5/frame_18.png | Bin .../nsfw}/lvl_5/frame_19.png | Bin .../Anims => external/nsfw}/lvl_5/frame_2.png | Bin .../nsfw}/lvl_5/frame_20.png | Bin .../nsfw}/lvl_5/frame_21.png | Bin .../nsfw}/lvl_5/frame_22.png | Bin .../nsfw}/lvl_5/frame_23.png | Bin .../nsfw}/lvl_5/frame_24.png | Bin .../nsfw}/lvl_5/frame_25.png | Bin .../nsfw}/lvl_5/frame_26.png | Bin .../nsfw}/lvl_5/frame_27.png | Bin .../Anims => external/nsfw}/lvl_5/frame_3.png | Bin .../Anims => external/nsfw}/lvl_5/frame_4.png | Bin .../Anims => external/nsfw}/lvl_5/frame_5.png | Bin .../Anims => external/nsfw}/lvl_5/frame_6.png | Bin .../Anims => external/nsfw}/lvl_5/frame_7.png | Bin .../Anims => external/nsfw}/lvl_5/frame_8.png | Bin .../Anims => external/nsfw}/lvl_5/frame_9.png | Bin .../Anims => external/nsfw}/lvl_5/meta.txt | 0 .../Anims => external/nsfw}/lvl_6/frame_0.png | Bin .../Anims => external/nsfw}/lvl_6/frame_1.png | Bin .../Anims => external/nsfw}/lvl_6/frame_2.png | Bin .../Anims => external/nsfw}/lvl_6/frame_3.png | Bin .../Anims => external/nsfw}/lvl_6/frame_4.png | Bin .../Anims => external/nsfw}/lvl_6/frame_5.png | Bin .../Anims => external/nsfw}/lvl_6/frame_6.png | Bin .../Anims => external/nsfw}/lvl_6/meta.txt | 0 .../Anims => external/nsfw}/lvl_7/frame_0.png | Bin .../Anims => external/nsfw}/lvl_7/frame_1.png | Bin .../nsfw}/lvl_7/frame_10.png | Bin .../nsfw}/lvl_7/frame_11.png | Bin .../nsfw}/lvl_7/frame_12.png | Bin .../nsfw}/lvl_7/frame_13.png | Bin .../Anims => external/nsfw}/lvl_7/frame_2.png | Bin .../Anims => external/nsfw}/lvl_7/frame_3.png | Bin .../Anims => external/nsfw}/lvl_7/frame_4.png | Bin .../Anims => external/nsfw}/lvl_7/frame_5.png | Bin .../Anims => external/nsfw}/lvl_7/frame_6.png | Bin .../Anims => external/nsfw}/lvl_7/frame_7.png | Bin .../Anims => external/nsfw}/lvl_7/frame_8.png | Bin .../Anims => external/nsfw}/lvl_7/frame_9.png | Bin .../Anims => external/nsfw}/lvl_7/meta.txt | 0 .../Anims => external/nsfw}/lvl_8/frame_0.png | Bin .../Anims => external/nsfw}/lvl_8/frame_1.png | Bin .../Anims => external/nsfw}/lvl_8/frame_2.png | Bin .../Anims => external/nsfw}/lvl_8/frame_3.png | Bin .../Anims => external/nsfw}/lvl_8/frame_4.png | Bin .../Anims => external/nsfw}/lvl_8/frame_5.png | Bin .../Anims => external/nsfw}/lvl_8/meta.txt | 0 .../Anims => external/nsfw}/lvl_9/frame_0.png | Bin .../Anims => external/nsfw}/lvl_9/frame_1.png | Bin .../Anims => external/nsfw}/lvl_9/frame_2.png | Bin .../Anims => external/nsfw}/lvl_9/frame_3.png | Bin .../Anims => external/nsfw}/lvl_9/frame_4.png | Bin .../Anims => external/nsfw}/lvl_9/frame_5.png | Bin .../Anims => external/nsfw}/lvl_9/frame_6.png | Bin .../Anims => external/nsfw}/lvl_9/frame_7.png | Bin .../Anims => external/nsfw}/lvl_9/meta.txt | 0 .../NSFW/Anims => external/nsfw}/manifest.txt | 0 .../{ => sfw}/L1_Boxing_128x64/frame_0.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_1.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_2.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_3.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_4.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_5.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_6.png | Bin .../{ => sfw}/L1_Boxing_128x64/meta.txt | 0 .../{ => sfw}/L1_Cry_128x64/frame_0.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_1.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_2.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_3.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_4.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_5.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_6.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_7.png | Bin .../external/{ => sfw}/L1_Cry_128x64/meta.txt | 0 .../{ => sfw}/L1_Furippa1_128x64/frame_0.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_1.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_10.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_11.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_12.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_13.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_14.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_15.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_16.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_17.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_18.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_2.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_3.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_4.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_5.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_6.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_7.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_8.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_9.png | Bin .../{ => sfw}/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.png | Bin .../L1_Happy_holidays_128x64/frame_1.png | Bin .../L1_Happy_holidays_128x64/frame_10.png | Bin .../L1_Happy_holidays_128x64/frame_11.png | Bin .../L1_Happy_holidays_128x64/frame_12.png | Bin .../L1_Happy_holidays_128x64/frame_2.png | Bin .../L1_Happy_holidays_128x64/frame_3.png | Bin .../L1_Happy_holidays_128x64/frame_4.png | Bin .../L1_Happy_holidays_128x64/frame_5.png | Bin .../L1_Happy_holidays_128x64/frame_6.png | Bin .../L1_Happy_holidays_128x64/frame_7.png | Bin .../L1_Happy_holidays_128x64/frame_8.png | Bin .../L1_Happy_holidays_128x64/frame_9.png | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{ => sfw}/L1_Laptop_128x51/frame_0.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_1.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_2.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_3.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_4.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_5.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_6.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_7.png | Bin .../{ => sfw}/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.png | Bin .../L1_Leaving_sad_128x64/frame_1.png | Bin .../L1_Leaving_sad_128x64/frame_10.png | Bin .../L1_Leaving_sad_128x64/frame_11.png | Bin .../L1_Leaving_sad_128x64/frame_12.png | Bin .../L1_Leaving_sad_128x64/frame_2.png | Bin .../L1_Leaving_sad_128x64/frame_3.png | Bin .../L1_Leaving_sad_128x64/frame_4.png | Bin .../L1_Leaving_sad_128x64/frame_5.png | Bin .../L1_Leaving_sad_128x64/frame_6.png | Bin .../L1_Leaving_sad_128x64/frame_7.png | Bin .../L1_Leaving_sad_128x64/frame_8.png | Bin .../L1_Leaving_sad_128x64/frame_9.png | Bin .../{ => sfw}/L1_Leaving_sad_128x64/meta.txt | 0 .../{ => sfw}/L1_Mad_fist_128x64/frame_0.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_1.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_10.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_11.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_12.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_13.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_2.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_3.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_4.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_5.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_6.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_7.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_8.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_9.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/meta.txt | 0 .../{ => sfw}/L1_Mods_128x64/frame_0.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_1.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_10.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_11.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_12.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_13.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_14.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_15.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_16.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_17.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_18.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_19.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_2.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_20.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_21.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_22.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_23.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_24.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_25.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_26.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_27.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_28.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_29.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_3.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_30.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_31.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_32.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_33.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_34.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_35.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_36.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_37.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_38.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_39.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_4.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_40.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_5.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_6.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_7.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_8.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_9.png | Bin .../{ => sfw}/L1_Mods_128x64/meta.txt | 0 .../{ => sfw}/L1_Painting_128x64/frame_0.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_1.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_10.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_11.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_2.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_3.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_4.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_5.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_6.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_7.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_8.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_9.png | Bin .../{ => sfw}/L1_Painting_128x64/meta.txt | 0 .../L1_Read_books_128x64/frame_0.png | Bin .../L1_Read_books_128x64/frame_1.png | Bin .../L1_Read_books_128x64/frame_2.png | Bin .../L1_Read_books_128x64/frame_3.png | Bin .../L1_Read_books_128x64/frame_4.png | Bin .../L1_Read_books_128x64/frame_5.png | Bin .../L1_Read_books_128x64/frame_6.png | Bin .../L1_Read_books_128x64/frame_7.png | Bin .../L1_Read_books_128x64/frame_8.png | Bin .../{ => sfw}/L1_Read_books_128x64/meta.txt | 0 .../{ => sfw}/L1_Recording_128x51/frame_0.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_1.png | Bin .../L1_Recording_128x51/frame_10.png | Bin .../L1_Recording_128x51/frame_11.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_2.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_3.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_4.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_5.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_6.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_7.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_8.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_9.png | Bin .../{ => sfw}/L1_Recording_128x51/meta.txt | 0 .../{ => sfw}/L1_Sleep_128x64/frame_0.png | Bin .../{ => sfw}/L1_Sleep_128x64/frame_1.png | Bin .../{ => sfw}/L1_Sleep_128x64/frame_2.png | Bin .../{ => sfw}/L1_Sleep_128x64/frame_3.png | Bin .../{ => sfw}/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.png | Bin .../L1_Sleigh_ride_128x64/frame_1.png | Bin .../L1_Sleigh_ride_128x64/frame_10.png | Bin .../L1_Sleigh_ride_128x64/frame_11.png | Bin .../L1_Sleigh_ride_128x64/frame_12.png | Bin .../L1_Sleigh_ride_128x64/frame_13.png | Bin .../L1_Sleigh_ride_128x64/frame_14.png | Bin .../L1_Sleigh_ride_128x64/frame_15.png | Bin .../L1_Sleigh_ride_128x64/frame_16.png | Bin .../L1_Sleigh_ride_128x64/frame_17.png | Bin .../L1_Sleigh_ride_128x64/frame_18.png | Bin .../L1_Sleigh_ride_128x64/frame_19.png | Bin .../L1_Sleigh_ride_128x64/frame_2.png | Bin .../L1_Sleigh_ride_128x64/frame_20.png | Bin .../L1_Sleigh_ride_128x64/frame_21.png | Bin .../L1_Sleigh_ride_128x64/frame_22.png | Bin .../L1_Sleigh_ride_128x64/frame_23.png | Bin .../L1_Sleigh_ride_128x64/frame_24.png | Bin .../L1_Sleigh_ride_128x64/frame_25.png | Bin .../L1_Sleigh_ride_128x64/frame_26.png | Bin .../L1_Sleigh_ride_128x64/frame_27.png | Bin .../L1_Sleigh_ride_128x64/frame_28.png | Bin .../L1_Sleigh_ride_128x64/frame_29.png | Bin .../L1_Sleigh_ride_128x64/frame_3.png | Bin .../L1_Sleigh_ride_128x64/frame_30.png | Bin .../L1_Sleigh_ride_128x64/frame_31.png | Bin .../L1_Sleigh_ride_128x64/frame_32.png | Bin .../L1_Sleigh_ride_128x64/frame_33.png | Bin .../L1_Sleigh_ride_128x64/frame_34.png | Bin .../L1_Sleigh_ride_128x64/frame_35.png | Bin .../L1_Sleigh_ride_128x64/frame_36.png | Bin .../L1_Sleigh_ride_128x64/frame_4.png | Bin .../L1_Sleigh_ride_128x64/frame_5.png | Bin .../L1_Sleigh_ride_128x64/frame_6.png | Bin .../L1_Sleigh_ride_128x64/frame_7.png | Bin .../L1_Sleigh_ride_128x64/frame_8.png | Bin .../L1_Sleigh_ride_128x64/frame_9.png | Bin .../{ => sfw}/L1_Sleigh_ride_128x64/meta.txt | 0 .../{ => sfw}/L1_Waves_128x50/frame_0.png | Bin .../{ => sfw}/L1_Waves_128x50/frame_1.png | Bin .../{ => sfw}/L1_Waves_128x50/frame_2.png | Bin .../{ => sfw}/L1_Waves_128x50/frame_3.png | Bin .../{ => sfw}/L1_Waves_128x50/meta.txt | 0 .../{ => sfw}/L2_Furippa2_128x64/frame_0.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_1.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_10.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_11.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_12.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_13.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_14.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_15.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_16.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_17.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_18.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_2.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_3.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_4.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_5.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_6.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_7.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_8.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_9.png | Bin .../{ => sfw}/L2_Furippa2_128x64/meta.txt | 0 .../L2_Hacking_pc_128x64/frame_0.png | Bin .../L2_Hacking_pc_128x64/frame_1.png | Bin .../L2_Hacking_pc_128x64/frame_2.png | Bin .../L2_Hacking_pc_128x64/frame_3.png | Bin .../L2_Hacking_pc_128x64/frame_4.png | Bin .../{ => sfw}/L2_Hacking_pc_128x64/meta.txt | 0 .../{ => sfw}/L2_Soldering_128x64/frame_0.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_1.png | Bin .../L2_Soldering_128x64/frame_10.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_2.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_3.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_4.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_5.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_6.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_7.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_8.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_9.png | Bin .../{ => sfw}/L2_Soldering_128x64/meta.txt | 0 .../{ => sfw}/L2_Wake_up_128x64/frame_0.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_1.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_10.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_11.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_12.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_13.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_14.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_15.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_16.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_17.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_18.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_19.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_2.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_20.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_3.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_4.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_5.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_6.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_7.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_8.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_9.png | Bin .../{ => sfw}/L2_Wake_up_128x64/meta.txt | 0 .../{ => sfw}/L3_Furippa3_128x64/frame_0.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_1.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_10.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_11.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_12.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_13.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_14.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_15.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_16.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_17.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_18.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_2.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_3.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_4.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_5.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_6.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_7.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_8.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_9.png | Bin .../{ => sfw}/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.png | Bin .../L3_Hijack_radio_128x64/frame_1.png | Bin .../L3_Hijack_radio_128x64/frame_10.png | Bin .../L3_Hijack_radio_128x64/frame_11.png | Bin .../L3_Hijack_radio_128x64/frame_12.png | Bin .../L3_Hijack_radio_128x64/frame_13.png | Bin .../L3_Hijack_radio_128x64/frame_2.png | Bin .../L3_Hijack_radio_128x64/frame_3.png | Bin .../L3_Hijack_radio_128x64/frame_4.png | Bin .../L3_Hijack_radio_128x64/frame_5.png | Bin .../L3_Hijack_radio_128x64/frame_6.png | Bin .../L3_Hijack_radio_128x64/frame_7.png | Bin .../L3_Hijack_radio_128x64/frame_8.png | Bin .../L3_Hijack_radio_128x64/frame_9.png | Bin .../{ => sfw}/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.png | Bin .../L3_Lab_research_128x54/frame_1.png | Bin .../L3_Lab_research_128x54/frame_10.png | Bin .../L3_Lab_research_128x54/frame_11.png | Bin .../L3_Lab_research_128x54/frame_12.png | Bin .../L3_Lab_research_128x54/frame_13.png | Bin .../L3_Lab_research_128x54/frame_2.png | Bin .../L3_Lab_research_128x54/frame_3.png | Bin .../L3_Lab_research_128x54/frame_4.png | Bin .../L3_Lab_research_128x54/frame_5.png | Bin .../L3_Lab_research_128x54/frame_6.png | Bin .../L3_Lab_research_128x54/frame_7.png | Bin .../L3_Lab_research_128x54/frame_8.png | Bin .../L3_Lab_research_128x54/frame_9.png | Bin .../{ => sfw}/L3_Lab_research_128x54/meta.txt | 0 .../dolphin/external/{ => sfw}/manifest.txt | 0 .../Animations/Levelup1_128x64}/frame_00.png | Bin .../Animations/Levelup1_128x64}/frame_01.png | Bin .../Animations/Levelup1_128x64}/frame_02.png | Bin .../Animations/Levelup1_128x64}/frame_03.png | Bin .../Animations/Levelup1_128x64}/frame_04.png | Bin .../Animations/Levelup1_128x64}/frame_05.png | Bin .../Animations/Levelup1_128x64}/frame_06.png | Bin .../Animations/Levelup1_128x64}/frame_07.png | Bin .../Animations/Levelup1_128x64}/frame_08.png | Bin .../Animations/Levelup1_128x64}/frame_09.png | Bin .../Animations/Levelup1_128x64}/frame_10.png | Bin .../Animations/Levelup1_128x64}/frame_11.png | Bin .../Animations/Levelup1_128x64}/frame_12.png | Bin .../Animations/Levelup1_128x64}/frame_13.png | Bin .../Animations/Levelup1_128x64}/frame_14.png | Bin .../Animations/Levelup1_128x64}/frame_15.png | Bin .../Animations/Levelup1_128x64}/frame_16.png | Bin .../Animations/Levelup1_128x64}/frame_17.png | Bin .../Animations/Levelup1_128x64}/frame_18.png | Bin .../Animations/Levelup1_128x64}/frame_19.png | Bin .../Animations/Levelup1_128x64}/frame_20.png | Bin .../Animations/Levelup1_128x64}/frame_21.png | Bin .../Animations/Levelup1_128x64}/frame_22.png | Bin .../Animations/Levelup1_128x64}/frame_23.png | Bin .../Animations/Levelup1_128x64}/frame_24.png | Bin .../Animations/Levelup1_128x64}/frame_25.png | Bin .../Animations/Levelup1_128x64}/frame_26.png | Bin .../Animations/Levelup1_128x64}/frame_27.png | Bin .../Animations/Levelup1_128x64}/frame_28.png | Bin .../Animations/Levelup1_128x64}/frame_29.png | Bin .../Animations/Levelup1_128x64}/frame_30.png | Bin .../Animations/Levelup1_128x64}/frame_31.png | Bin .../Animations/Levelup1_128x64}/frame_rate | 0 .../Levelup1_128x64_sfw/frame_00.png | Bin 0 -> 1326 bytes .../Levelup1_128x64_sfw/frame_01.png | Bin 0 -> 1597 bytes .../Levelup1_128x64_sfw/frame_02.png | Bin 0 -> 1754 bytes .../Levelup1_128x64_sfw/frame_03.png | Bin 0 -> 1828 bytes .../Levelup1_128x64_sfw/frame_04.png | Bin 0 -> 1686 bytes .../Levelup1_128x64_sfw/frame_05.png | Bin 0 -> 1672 bytes .../Levelup1_128x64_sfw/frame_06.png | Bin 0 -> 1659 bytes .../Levelup1_128x64_sfw/frame_07.png | Bin 0 -> 1540 bytes .../Levelup1_128x64_sfw/frame_08.png | Bin 0 -> 1557 bytes .../Levelup1_128x64_sfw/frame_09.png | Bin 0 -> 1551 bytes .../Levelup1_128x64_sfw/frame_10.png | Bin 0 -> 1604 bytes .../frame_rate | 0 .../frame_00.png | Bin .../frame_01.png | Bin .../frame_02.png | Bin .../frame_03.png | Bin .../frame_04.png | Bin .../frame_05.png | Bin .../frame_06.png | Bin .../frame_07.png | Bin .../frame_08.png | Bin .../frame_09.png | Bin .../frame_10.png | Bin .../Animations/Levelup2_128x64_sfw/frame_rate | 1 + assets/icons/BLE/BLE_Pairing_128x64.png | Bin 2307 -> 2610 bytes assets/icons/BLE/BLE_Pairing_128x64_sfw.png | Bin 0 -> 2307 bytes assets/icons/Dolphin/DolphinCommon_56x48.png | Bin 1416 -> 3376 bytes .../icons/Dolphin/DolphinCommon_56x48_sfw.png | Bin 0 -> 1416 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 1177 -> 5390 bytes .../DolphinReadingSuccess_59x63_sfw.png | Bin 0 -> 1177 bytes assets/icons/Interface/SmallArrowDown_4x7.png | Bin 0 -> 8340 bytes assets/icons/Interface/SmallArrowUp_4x7.png | Bin 0 -> 8552 bytes assets/icons/NFC/ArrowC_1_36x36.png | Bin 0 -> 3692 bytes assets/icons/NFC/Detailed_chip_17x13.png | Bin 0 -> 981 bytes assets/icons/NFC/Medium-chip-22x21.png | Bin 0 -> 3740 bytes .../icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 1541 -> 4224 bytes .../NFC/NFC_dolphin_emulation_47x61_sfw.png | Bin 0 -> 1541 bytes assets/icons/NFC/Tap_reader_36x38.png | Bin 0 -> 3748 bytes .../Passport/flipper.png} | Bin assets/icons/Passport/passport_DB.png | Bin 750 -> 852 bytes assets/icons/Passport/passport_DB_sfw.png | Bin 0 -> 750 bytes .../Passport/passport_bad1_46x49_sfw.png | Bin 0 -> 1237 bytes ..._46x49.png => passport_bad2_46x49_sfw.png} | Bin .../Passport/passport_bad3_46x49_sfw.png | Bin 0 -> 1304 bytes .../Passport/passport_happy1_46x49_sfw.png | Bin 0 -> 1296 bytes ...6x49.png => passport_happy2_46x49_sfw.png} | Bin .../Passport/passport_happy3_46x49_sfw.png | Bin 0 -> 1348 bytes .../Passport/passport_okay1_46x49_sfw.png | Bin 0 -> 1244 bytes ...46x49.png => passport_okay2_46x49_sfw.png} | Bin .../Passport/passport_okay3_46x49_sfw.png | Bin 0 -> 1304 bytes .../icons/RFID/RFIDDolphinReceive_97x61.png | Bin 1421 -> 4862 bytes .../RFID/RFIDDolphinReceive_97x61_sfw.png | Bin 0 -> 1421 bytes assets/icons/RFID/RFIDDolphinSend_97x61.png | Bin 1418 -> 4882 bytes .../icons/RFID/RFIDDolphinSend_97x61_sfw.png | Bin 0 -> 1418 bytes .../icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 2681 -> 4466 bytes .../RFID/RFIDDolphinSuccess_108x57_sfw.png | Bin 0 -> 2681 bytes assets/icons/Settings/Cry_dolph_55x52.png | Bin 3898 -> 3798 bytes assets/icons/Settings/Cry_dolph_55x52_sfw.png | Bin 0 -> 3898 bytes assets/icons/StatusBar/Alert_9x8.png | Bin 0 -> 3611 bytes assets/icons/StatusBar/Attention_5x8.png | Bin 0 -> 1690 bytes ...g_9x10.png => Charging-lightning_9x10.png} | Bin ...0.png => Charging-lightning_mask_9x10.png} | Bin assets/icons/StatusBar/GameMode_11x8.png | Bin 0 -> 3610 bytes assets/icons/SubGhz/Scanning_123x52.png | Bin 1690 -> 4092 bytes assets/icons/SubGhz/Scanning_123x52_sfw.png | Bin 0 -> 1690 bytes assets/icons/U2F/Auth_62x31.png | Bin 3761 -> 1864 bytes assets/icons/U2F/Auth_62x31_sfw.png | Bin 0 -> 3761 bytes assets/icons/U2F/Connect_me_62x31.png | Bin 3767 -> 1895 bytes assets/icons/U2F/Connect_me_62x31_sfw.png | Bin 0 -> 3767 bytes assets/icons/U2F/Connected_62x31.png | Bin 3765 -> 1874 bytes assets/icons/U2F/Connected_62x31_sfw.png | Bin 0 -> 3765 bytes assets/icons/U2F/Error_62x31.png | Bin 3751 -> 1863 bytes assets/icons/U2F/Error_62x31_sfw.png | Bin 0 -> 3751 bytes assets/icons/iButton/DolphinMafia_115x62.png | Bin 2504 -> 6876 bytes .../icons/iButton/DolphinMafia_115x62_sfw.png | Bin 0 -> 2504 bytes assets/icons/iButton/DolphinNice_96x59.png | Bin 2459 -> 5422 bytes .../icons/iButton/DolphinNice_96x59_sfw.png | Bin 0 -> 2459 bytes assets/icons/iButton/DolphinWait_61x59.png | Bin 2023 -> 5122 bytes .../icons/iButton/DolphinWait_61x59_sfw.png | Bin 0 -> 2023 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 2157 -> 4719 bytes .../iButtonDolphinVerySuccess_108x52_sfw.png | Bin 0 -> 2157 bytes .../nsfw}/PaxGod_TikTok_Marketing/frame_0.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_1.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_10.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_11.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_12.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_13.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_14.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_15.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_16.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_17.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_18.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_19.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_2.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_20.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_21.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_3.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_4.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_5.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_6.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_7.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_8.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_9.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/meta.txt | 3 +- .../Anims => dolphin/nsfw}/lvl_1/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_10/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_11/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_32.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_33.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_34.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_35.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_36.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_37.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_38.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_39.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_40.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_41.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_42.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_43.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_44.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_45.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_46.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_47.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_48.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_49.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_12/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_13/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_14/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_15/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_16/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_17/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_18/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_19/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_2/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_20/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_21/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_22/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_32.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_33.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_34.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_35.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_36.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_37.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_38.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_39.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_40.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_41.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_42.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_43.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_44.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_45.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_46.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_47.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_48.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_49.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_50.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_51.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_52.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_53.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_54.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_55.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_56.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_57.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_58.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_59.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_23/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_24/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_25/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_32.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_33.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_34.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_35.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_26/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_27/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_28/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_29/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_32.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_33.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_34.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_35.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_36.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_37.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_38.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_39.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_40.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_41.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_42.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_43.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_44.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_45.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_46.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_47.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_48.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_49.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_50.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_51.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_3/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_30/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_32.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_33.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_34.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_35.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_36.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_37.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_38.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_39.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_40.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_41.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_42.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_43.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_44.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_45.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_46.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_47.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_48.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_49.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_4/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_5/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_6/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_7/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/meta.txt | 2 +- .../Anims => dolphin/nsfw}/lvl_8/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_9/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/meta.txt | 0 .../NSFW/Anims => dolphin/nsfw}/manifest.txt | 0 .../{ => sfw}/L1_Boxing_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Boxing_128x64/meta.txt | 0 .../{ => sfw}/L1_Cry_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_7.bm | Bin .../dolphin/{ => sfw}/L1_Cry_128x64/meta.txt | 0 .../{ => sfw}/L1_Furippa1_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_10.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_11.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_12.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_13.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_14.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_15.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_16.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_17.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_18.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_7.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_8.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_9.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.bm | Bin .../L1_Happy_holidays_128x64/frame_1.bm | Bin .../L1_Happy_holidays_128x64/frame_10.bm | Bin .../L1_Happy_holidays_128x64/frame_11.bm | Bin .../L1_Happy_holidays_128x64/frame_12.bm | Bin .../L1_Happy_holidays_128x64/frame_2.bm | Bin .../L1_Happy_holidays_128x64/frame_3.bm | Bin .../L1_Happy_holidays_128x64/frame_4.bm | Bin .../L1_Happy_holidays_128x64/frame_5.bm | Bin .../L1_Happy_holidays_128x64/frame_6.bm | Bin .../L1_Happy_holidays_128x64/frame_7.bm | Bin .../L1_Happy_holidays_128x64/frame_8.bm | Bin .../L1_Happy_holidays_128x64/frame_9.bm | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{ => sfw}/L1_Laptop_128x51/frame_0.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_1.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_2.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_3.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_4.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_5.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_6.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_7.bm | Bin .../{ => sfw}/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.bm | Bin .../L1_Leaving_sad_128x64/frame_1.bm | Bin .../L1_Leaving_sad_128x64/frame_10.bm | Bin .../L1_Leaving_sad_128x64/frame_11.bm | Bin .../L1_Leaving_sad_128x64/frame_12.bm | Bin .../L1_Leaving_sad_128x64/frame_2.bm | Bin .../L1_Leaving_sad_128x64/frame_3.bm | Bin .../L1_Leaving_sad_128x64/frame_4.bm | Bin .../L1_Leaving_sad_128x64/frame_5.bm | Bin .../L1_Leaving_sad_128x64/frame_6.bm | Bin .../L1_Leaving_sad_128x64/frame_7.bm | Bin .../L1_Leaving_sad_128x64/frame_8.bm | Bin .../L1_Leaving_sad_128x64/frame_9.bm | Bin .../{ => sfw}/L1_Leaving_sad_128x64/meta.txt | 0 .../{ => sfw}/L1_Mad_fist_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_10.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_11.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_12.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_13.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_7.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_8.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_9.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/meta.txt | 0 .../{ => sfw}/L1_Mods_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_10.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_11.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_12.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_13.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_14.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_15.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_16.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_17.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_18.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_19.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_20.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_21.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_22.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_23.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_24.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_25.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_26.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_27.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_28.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_29.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_30.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_31.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_32.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_33.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_34.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_35.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_36.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_37.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_38.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_39.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_40.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_7.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_8.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_9.bm | Bin .../dolphin/{ => sfw}/L1_Mods_128x64/meta.txt | 0 .../{ => sfw}/L1_Painting_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_10.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_11.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_7.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_8.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_9.bm | Bin .../{ => sfw}/L1_Painting_128x64/meta.txt | 0 .../{ => sfw}/L1_Read_books_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_7.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_8.bm | Bin .../{ => sfw}/L1_Read_books_128x64/meta.txt | 0 .../{ => sfw}/L1_Recording_128x51/frame_0.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_1.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_10.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_11.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_2.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_3.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_4.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_5.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_6.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_7.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_8.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_9.bm | Bin .../{ => sfw}/L1_Recording_128x51/meta.txt | 0 .../{ => sfw}/L1_Sleep_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Sleep_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Sleep_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Sleep_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.bm | Bin .../L1_Sleigh_ride_128x64/frame_1.bm | Bin .../L1_Sleigh_ride_128x64/frame_10.bm | Bin .../L1_Sleigh_ride_128x64/frame_11.bm | Bin .../L1_Sleigh_ride_128x64/frame_12.bm | Bin .../L1_Sleigh_ride_128x64/frame_13.bm | Bin .../L1_Sleigh_ride_128x64/frame_14.bm | Bin .../L1_Sleigh_ride_128x64/frame_15.bm | Bin .../L1_Sleigh_ride_128x64/frame_16.bm | Bin .../L1_Sleigh_ride_128x64/frame_17.bm | Bin .../L1_Sleigh_ride_128x64/frame_18.bm | Bin .../L1_Sleigh_ride_128x64/frame_19.bm | Bin .../L1_Sleigh_ride_128x64/frame_2.bm | Bin .../L1_Sleigh_ride_128x64/frame_20.bm | Bin .../L1_Sleigh_ride_128x64/frame_21.bm | Bin .../L1_Sleigh_ride_128x64/frame_22.bm | Bin .../L1_Sleigh_ride_128x64/frame_23.bm | Bin .../L1_Sleigh_ride_128x64/frame_24.bm | Bin .../L1_Sleigh_ride_128x64/frame_25.bm | Bin .../L1_Sleigh_ride_128x64/frame_26.bm | Bin .../L1_Sleigh_ride_128x64/frame_27.bm | Bin .../L1_Sleigh_ride_128x64/frame_28.bm | Bin .../L1_Sleigh_ride_128x64/frame_29.bm | Bin .../L1_Sleigh_ride_128x64/frame_3.bm | Bin .../L1_Sleigh_ride_128x64/frame_30.bm | Bin .../L1_Sleigh_ride_128x64/frame_31.bm | Bin .../L1_Sleigh_ride_128x64/frame_32.bm | Bin .../L1_Sleigh_ride_128x64/frame_33.bm | Bin .../L1_Sleigh_ride_128x64/frame_34.bm | Bin .../L1_Sleigh_ride_128x64/frame_35.bm | Bin .../L1_Sleigh_ride_128x64/frame_36.bm | Bin .../L1_Sleigh_ride_128x64/frame_4.bm | Bin .../L1_Sleigh_ride_128x64/frame_5.bm | Bin .../L1_Sleigh_ride_128x64/frame_6.bm | Bin .../L1_Sleigh_ride_128x64/frame_7.bm | Bin .../L1_Sleigh_ride_128x64/frame_8.bm | Bin .../L1_Sleigh_ride_128x64/frame_9.bm | Bin .../{ => sfw}/L1_Sleigh_ride_128x64/meta.txt | 0 .../{ => sfw}/L1_Waves_128x50/frame_0.bm | Bin .../{ => sfw}/L1_Waves_128x50/frame_1.bm | Bin .../{ => sfw}/L1_Waves_128x50/frame_2.bm | Bin .../{ => sfw}/L1_Waves_128x50/frame_3.bm | Bin .../{ => sfw}/L1_Waves_128x50/meta.txt | 0 .../{ => sfw}/L2_Furippa2_128x64/frame_0.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_1.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_10.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_11.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_12.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_13.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_14.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_15.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_16.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_17.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_18.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_2.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_3.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_4.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_5.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_6.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_7.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_8.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_9.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/meta.txt | 0 .../{ => sfw}/L2_Hacking_pc_128x64/frame_0.bm | Bin .../{ => sfw}/L2_Hacking_pc_128x64/frame_1.bm | Bin .../{ => sfw}/L2_Hacking_pc_128x64/frame_2.bm | Bin .../{ => sfw}/L2_Hacking_pc_128x64/frame_3.bm | Bin .../{ => sfw}/L2_Hacking_pc_128x64/frame_4.bm | Bin .../{ => sfw}/L2_Hacking_pc_128x64/meta.txt | 0 .../{ => sfw}/L2_Soldering_128x64/frame_0.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_1.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_10.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_2.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_3.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_4.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_5.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_6.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_7.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_8.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_9.bm | Bin .../{ => sfw}/L2_Soldering_128x64/meta.txt | 0 .../{ => sfw}/L2_Wake_up_128x64/frame_0.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_1.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_10.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_11.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_12.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_13.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_14.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_15.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_16.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_17.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_18.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_19.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_2.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_20.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_3.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_4.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_5.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_6.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_7.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_8.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_9.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/meta.txt | 0 .../{ => sfw}/L3_Furippa3_128x64/frame_0.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_1.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_10.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_11.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_12.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_13.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_14.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_15.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_16.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_17.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_18.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_2.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_3.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_4.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_5.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_6.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_7.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_8.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_9.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.bm | Bin .../L3_Hijack_radio_128x64/frame_1.bm | Bin .../L3_Hijack_radio_128x64/frame_10.bm | Bin .../L3_Hijack_radio_128x64/frame_11.bm | Bin .../L3_Hijack_radio_128x64/frame_12.bm | Bin .../L3_Hijack_radio_128x64/frame_13.bm | Bin .../L3_Hijack_radio_128x64/frame_2.bm | Bin .../L3_Hijack_radio_128x64/frame_3.bm | Bin .../L3_Hijack_radio_128x64/frame_4.bm | Bin .../L3_Hijack_radio_128x64/frame_5.bm | Bin .../L3_Hijack_radio_128x64/frame_6.bm | Bin .../L3_Hijack_radio_128x64/frame_7.bm | Bin .../L3_Hijack_radio_128x64/frame_8.bm | Bin .../L3_Hijack_radio_128x64/frame_9.bm | Bin .../{ => sfw}/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.bm | Bin .../L3_Lab_research_128x54/frame_1.bm | Bin .../L3_Lab_research_128x54/frame_10.bm | Bin .../L3_Lab_research_128x54/frame_11.bm | Bin .../L3_Lab_research_128x54/frame_12.bm | Bin .../L3_Lab_research_128x54/frame_13.bm | Bin .../L3_Lab_research_128x54/frame_2.bm | Bin .../L3_Lab_research_128x54/frame_3.bm | Bin .../L3_Lab_research_128x54/frame_4.bm | Bin .../L3_Lab_research_128x54/frame_5.bm | Bin .../L3_Lab_research_128x54/frame_6.bm | Bin .../L3_Lab_research_128x54/frame_7.bm | Bin .../L3_Lab_research_128x54/frame_8.bm | Bin .../L3_Lab_research_128x54/frame_9.bm | Bin .../{ => sfw}/L3_Lab_research_128x54/meta.txt | 0 .../resources/dolphin/{ => sfw}/manifest.txt | 0 .../Anims/PaxGod_TikTok_Marketing/frame_22.bm | Bin 472 -> 0 bytes .../Anims/PaxGod_TikTok_Marketing/frame_23.bm | Bin 462 -> 0 bytes .../Anims/PaxGod_TikTok_Marketing/frame_24.bm | Bin 487 -> 0 bytes .../Anims/PaxGod_TikTok_Marketing/frame_25.bm | Bin 483 -> 0 bytes .../Anims/PaxGod_TikTok_Marketing/frame_26.bm | Bin 483 -> 0 bytes .../Anims/PaxGod_TikTok_Marketing/frame_27.bm | Bin 488 -> 0 bytes .../Animations/Levelup_128x64/frame_00.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_01.bm | Bin 217 -> 0 bytes .../Animations/Levelup_128x64/frame_02.bm | Bin 219 -> 0 bytes .../Animations/Levelup_128x64/frame_03.bm | Bin 224 -> 0 bytes .../Animations/Levelup_128x64/frame_04.bm | Bin 221 -> 0 bytes .../Animations/Levelup_128x64/frame_05.bm | Bin 223 -> 0 bytes .../Animations/Levelup_128x64/frame_06.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_07.bm | Bin 227 -> 0 bytes .../Animations/Levelup_128x64/frame_08.bm | Bin 228 -> 0 bytes .../Animations/Levelup_128x64/frame_09.bm | Bin 222 -> 0 bytes .../Animations/Levelup_128x64/frame_10.bm | Bin 224 -> 0 bytes .../Animations/Levelup_128x64/frame_11.bm | Bin 221 -> 0 bytes .../Animations/Levelup_128x64/frame_12.bm | Bin 223 -> 0 bytes .../Animations/Levelup_128x64/frame_13.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_14.bm | Bin 227 -> 0 bytes .../Animations/Levelup_128x64/frame_15.bm | Bin 228 -> 0 bytes .../Animations/Levelup_128x64/frame_16.bm | Bin 222 -> 0 bytes .../Animations/Levelup_128x64/frame_17.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_18.bm | Bin 217 -> 0 bytes .../Animations/Levelup_128x64/frame_19.bm | Bin 219 -> 0 bytes .../Animations/Levelup_128x64/frame_20.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_21.bm | Bin 217 -> 0 bytes .../Animations/Levelup_128x64/frame_22.bm | Bin 219 -> 0 bytes .../Animations/Levelup_128x64/frame_23.bm | Bin 224 -> 0 bytes .../Animations/Levelup_128x64/frame_24.bm | Bin 221 -> 0 bytes .../Animations/Levelup_128x64/frame_25.bm | Bin 223 -> 0 bytes .../Animations/Levelup_128x64/frame_26.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_27.bm | Bin 227 -> 0 bytes .../Animations/Levelup_128x64/frame_28.bm | Bin 228 -> 0 bytes .../Animations/Levelup_128x64/frame_29.bm | Bin 222 -> 0 bytes .../Animations/Levelup_128x64/frame_30.bm | Bin 224 -> 0 bytes .../Animations/Levelup_128x64/frame_31.bm | Bin 518 -> 0 bytes .../NSFW/Icons/Animations/Levelup_128x64/meta | Bin 16 -> 0 bytes .../NSFW/Icons/BLE/BLE_Pairing_128x64.bmx | Bin 480 -> 0 bytes .../Icons/Dolphin/DolphinCommon_56x48.bmx | Bin 325 -> 0 bytes .../Infrared/DolphinReadingSuccess_59x63.bmx | Bin 513 -> 0 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.bmx | Bin 375 -> 0 bytes .../NSFW/Icons/Passport/passport_DB.bmx | Bin 286 -> 0 bytes .../Icons/Passport/passport_bad_46x49.bmx | Bin 297 -> 0 bytes .../Icons/Passport/passport_happy_46x49.bmx | Bin 297 -> 0 bytes .../Icons/Passport/passport_okay_46x49.bmx | Bin 297 -> 0 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.bmx | Bin 525 -> 0 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx | Bin 525 -> 0 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.bmx | Bin 502 -> 0 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.bmx | Bin 352 -> 0 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.bmx | Bin 458 -> 0 bytes .../NSFW/Icons/U2F/Auth_62x31.bmx | Bin 257 -> 0 bytes .../NSFW/Icons/U2F/Connect_me_62x31.bmx | Bin 257 -> 0 bytes .../NSFW/Icons/U2F/Connected_62x31.bmx | Bin 257 -> 0 bytes .../NSFW/Icons/U2F/Error_62x31.bmx | Bin 257 -> 0 bytes .../Icons/iButton/DolphinMafia_115x62.bmx | Bin 684 -> 0 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.bmx | Bin 609 -> 0 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.bmx | Bin 481 -> 0 bytes .../iButtonDolphinVerySuccess_108x52.bmx | Bin 482 -> 0 bytes scripts/asset_packer.py | 70 ++--- scripts/assets.py | 35 ++- 2348 files changed, 298 insertions(+), 216 deletions(-) delete mode 100644 assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_DB.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_happy_46x49.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Auth_62x31.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connect_me_62x31.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Error_62x31.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinMafia_115x62.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinNice_96x59.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_32.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_33.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_34.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_35.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_36.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_37.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_38.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_39.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_40.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_41.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_42.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_43.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_44.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_45.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_46.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_47.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_48.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_49.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_32.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_33.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_34.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_35.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_36.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_37.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_38.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_39.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_40.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_41.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_42.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_43.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_44.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_45.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_46.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_47.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_48.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_49.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_50.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_51.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_52.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_53.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_54.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_55.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_56.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_57.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_58.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_59.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_32.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_33.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_34.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_35.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_32.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_33.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_34.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_35.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_36.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_37.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_38.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_39.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_40.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_41.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_42.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_43.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_44.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_45.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_46.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_47.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_48.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_49.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_50.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_51.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_32.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_33.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_34.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_35.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_36.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_37.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_38.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_39.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_40.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_41.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_42.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_43.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_44.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_45.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_46.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_47.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_48.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_49.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/manifest.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_19.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_20.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_21.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_22.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_23.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_24.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_25.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_26.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_27.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_28.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_29.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_30.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_31.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_32.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_33.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_34.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_35.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_36.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_37.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_38.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_39.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_40.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleep_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleep_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleep_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleep_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleep_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_19.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_20.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_21.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_22.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_23.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_24.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_25.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_26.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_27.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_28.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_29.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_30.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_31.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_32.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_33.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_34.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_35.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_36.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Waves_128x50/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Waves_128x50/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Waves_128x50/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Waves_128x50/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Waves_128x50/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_19.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_20.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/manifest.txt (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_00.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_01.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_02.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_03.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_04.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_05.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_06.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_07.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_08.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_09.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_10.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_11.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_12.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_13.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_14.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_15.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_16.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_17.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_18.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_19.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_20.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_21.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_22.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_23.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_24.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_25.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_26.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_27.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_28.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_29.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_30.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_31.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_rate (100%) create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_03.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_06.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_10.png rename assets/icons/Animations/{Levelup_128x64 => Levelup1_128x64_sfw}/frame_rate (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_00.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_01.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_02.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_03.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_04.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_05.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_06.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_07.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_08.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_09.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_10.png (100%) create mode 100644 assets/icons/Animations/Levelup2_128x64_sfw/frame_rate create mode 100644 assets/icons/BLE/BLE_Pairing_128x64_sfw.png create mode 100644 assets/icons/Dolphin/DolphinCommon_56x48_sfw.png create mode 100644 assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png create mode 100644 assets/icons/Interface/SmallArrowDown_4x7.png create mode 100644 assets/icons/Interface/SmallArrowUp_4x7.png create mode 100644 assets/icons/NFC/ArrowC_1_36x36.png create mode 100644 assets/icons/NFC/Detailed_chip_17x13.png create mode 100644 assets/icons/NFC/Medium-chip-22x21.png create mode 100644 assets/icons/NFC/NFC_dolphin_emulation_47x61_sfw.png create mode 100644 assets/icons/NFC/Tap_reader_36x38.png rename assets/{dolphin/custom/NSFW/Icons/Passport/passport_bad_46x49.png => icons/Passport/flipper.png} (100%) create mode 100644 assets/icons/Passport/passport_DB_sfw.png create mode 100644 assets/icons/Passport/passport_bad1_46x49_sfw.png rename assets/icons/Passport/{passport_bad_46x49.png => passport_bad2_46x49_sfw.png} (100%) create mode 100644 assets/icons/Passport/passport_bad3_46x49_sfw.png create mode 100644 assets/icons/Passport/passport_happy1_46x49_sfw.png rename assets/icons/Passport/{passport_happy_46x49.png => passport_happy2_46x49_sfw.png} (100%) create mode 100644 assets/icons/Passport/passport_happy3_46x49_sfw.png create mode 100644 assets/icons/Passport/passport_okay1_46x49_sfw.png rename assets/icons/Passport/{passport_okay_46x49.png => passport_okay2_46x49_sfw.png} (100%) create mode 100644 assets/icons/Passport/passport_okay3_46x49_sfw.png create mode 100644 assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png create mode 100644 assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png create mode 100644 assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png create mode 100644 assets/icons/Settings/Cry_dolph_55x52_sfw.png create mode 100644 assets/icons/StatusBar/Alert_9x8.png create mode 100644 assets/icons/StatusBar/Attention_5x8.png rename assets/icons/StatusBar/{Charging_lightning_9x10.png => Charging-lightning_9x10.png} (100%) rename assets/icons/StatusBar/{Charging_lightning_mask_9x10.png => Charging-lightning_mask_9x10.png} (100%) create mode 100644 assets/icons/StatusBar/GameMode_11x8.png create mode 100644 assets/icons/SubGhz/Scanning_123x52_sfw.png create mode 100644 assets/icons/U2F/Auth_62x31_sfw.png create mode 100644 assets/icons/U2F/Connect_me_62x31_sfw.png create mode 100644 assets/icons/U2F/Connected_62x31_sfw.png create mode 100644 assets/icons/U2F/Error_62x31_sfw.png create mode 100644 assets/icons/iButton/DolphinMafia_115x62_sfw.png create mode 100644 assets/icons/iButton/DolphinNice_96x59_sfw.png create mode 100644 assets/icons/iButton/DolphinWait_61x59_sfw.png create mode 100644 assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/meta.txt (93%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_32.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_33.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_34.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_35.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_36.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_37.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_38.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_39.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_40.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_41.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_42.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_43.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_44.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_45.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_46.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_47.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_48.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_49.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_32.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_33.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_34.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_35.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_36.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_37.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_38.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_39.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_40.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_41.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_42.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_43.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_44.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_45.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_46.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_47.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_48.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_49.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_50.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_51.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_52.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_53.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_54.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_55.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_56.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_57.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_58.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_59.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_32.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_33.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_34.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_35.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_32.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_33.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_34.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_35.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_36.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_37.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_38.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_39.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_40.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_41.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_42.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_43.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_44.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_45.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_46.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_47.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_48.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_49.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_50.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_51.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_32.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_33.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_34.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_35.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_36.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_37.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_38.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_39.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_40.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_41.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_42.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_43.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_44.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_45.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_46.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_47.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_48.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_49.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/meta.txt (92%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/manifest.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_37.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_38.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_39.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_40.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleep_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleep_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleep_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleep_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleep_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Waves_128x50/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Waves_128x50/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Waves_128x50/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Waves_128x50/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Waves_128x50/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/manifest.txt (100%) delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_bad_46x49.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinNice_96x59.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinWait_61x59.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx diff --git a/.gitignore b/.gitignore index 025246faa..bf7addba9 100644 --- a/.gitignore +++ b/.gitignore @@ -74,9 +74,7 @@ lib/STM32CubeWB # Asset packs assets/dolphin/custom/* -!assets/dolphin/custom/NSFW/ !assets/dolphin/custom/WatchDogs/ !assets/dolphin/custom/ReadMe.md assets/resources/dolphin_custom/* -!assets/resources/dolphin_custom/NSFW/ !assets/resources/dolphin_custom/WatchDogs/ diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 9cb66a5e5..3163b8cff 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -457,14 +457,10 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { browser->last_tab_switch_dir = key; - for(int i = 0; i < 2; i++) { - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; - } - if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) continue; - break; + if(key == InputKeyLeft) { + tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; + } else { + tab = (tab + 1) % ArchiveTabTotal; } browser->is_root = true; diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 43a9a651a..5b13e98da 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -17,7 +17,6 @@ static const char* tab_default_paths[] = { [ArchiveTabBadUsb] = ANY_PATH("badusb"), [ArchiveTabU2f] = "/app:u2f", [ArchiveTabApplications] = ANY_PATH("apps"), - [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX, [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, }; @@ -45,7 +44,6 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, [ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabApplications] = ArchiveFileTypeApplication, - [ArchiveTabInternal] = ArchiveFileTypeUnknown, [ArchiveTabBrowser] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 26ed17d75..dce753fde 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -19,7 +19,6 @@ static const char* ArchiveTabNames[] = { [ArchiveTabBadUsb] = "Bad USB", [ArchiveTabU2f] = "U2F", [ArchiveTabApplications] = "Apps", - [ArchiveTabInternal] = "Internal", [ArchiveTabBrowser] = "Browser", }; diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 6e6582405..a525a7db6 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -31,7 +31,6 @@ typedef enum { ArchiveTabBadUsb, ArchiveTabU2f, ArchiveTabApplications, - ArchiveTabInternal, ArchiveTabBrowser, ArchiveTabTotal, } ArchiveTabEnum; diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_usb/scenes/bad_usb_scene_error.c index d32eee382..2c707bbf1 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_error.c @@ -1,5 +1,5 @@ #include "../bad_usb_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" typedef enum { BadUsbCustomEventErrorBack, @@ -32,7 +32,7 @@ void bad_usb_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); } else if(app->error == BadUsbAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index 51bed3835..ad889cd1c 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -3,7 +3,7 @@ #include #include #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" #define MAX_NAME_LEN 64 @@ -28,6 +28,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); if(strlen(model->layout) == 0) { furi_string_set(disp_str, "(default)"); @@ -48,7 +49,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || (model->state.state == BadUsbStateNotConnected)) { - if(XTREME_ASSETS()->is_nsfw) { + if(xtreme_settings->nsfw_mode) { elements_button_center(canvas, "Cum"); } else { elements_button_center(canvas, "Start"); @@ -67,7 +68,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if(model->state.state == BadUsbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(XTREME_ASSETS()->is_nsfw) { + if(xtreme_settings->nsfw_mode) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy"); } else { @@ -77,7 +78,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { } else if(model->state.state == BadUsbStateWillRun) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(XTREME_ASSETS()->is_nsfw) { + if(xtreme_settings->nsfw_mode) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum"); } else { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index 35a6ce1d9..162faf2f1 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -1,5 +1,5 @@ #include "../u2f_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); @@ -27,7 +27,7 @@ void u2f_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); } else if(app->error == U2fAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index fc1c5c4fa..eecd32702 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -21,7 +21,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { if(model->display_msg == U2fMsgNotConnected) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connect_me_62x31); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { canvas_draw_str_aligned( canvas, 128 / 2, 3, AlignCenter, AlignTop, "Plug me in d-daddy"); } else { @@ -32,7 +32,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connected!"); } else if(model->display_msg == U2fMsgRegister) { - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -44,7 +44,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to register"); } } else if(model->display_msg == U2fMsgAuth) { - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -57,7 +57,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgSuccess) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Cum released~"); } else { canvas_draw_str_aligned( @@ -65,7 +65,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgError) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Error_62x31); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Unable to cum"); } else { canvas_draw_str_aligned( diff --git a/applications/plugins/nightstand/application.fam b/applications/plugins/nightstand/application.fam index cea02bd00..caec3cbf6 100644 --- a/applications/plugins/nightstand/application.fam +++ b/applications/plugins/nightstand/application.fam @@ -10,3 +10,4 @@ App( fap_category="Misc", order=81, ) + diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam index 750373342..27e73a0ce 100644 --- a/applications/plugins/pomodoro/application.fam +++ b/applications/plugins/pomodoro/application.fam @@ -9,4 +9,4 @@ App( fap_icon_assets="images", fap_icon="flipp_pomodoro_10.png", fap_icon_assets_symbol="flipp_pomodoro", -) +) \ No newline at end of file diff --git a/applications/plugins/scrambler/application.fam b/applications/plugins/scrambler/application.fam index 4d48d7bb5..8d87a4a62 100644 --- a/applications/plugins/scrambler/application.fam +++ b/applications/plugins/scrambler/application.fam @@ -1,6 +1,6 @@ # COMPILE ISTRUCTIONS: -# Clean the code and remove old binaries/compilation artefact +# Clean the code and remove old binaries/compilation artefact # ./fbt -c fap_rubiks_cube_scrambler # Compile FAP diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 0808a3618..5239d72d5 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -13,7 +13,7 @@ #include "animation_storage.h" #include "animation_manager.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" #define TAG "AnimationManager" @@ -569,6 +569,9 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m static void animation_manager_switch_to_one_shot_view(AnimationManager* animation_manager) { furi_assert(animation_manager); furi_assert(!animation_manager->one_shot_view); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); + DolphinStats stats = dolphin_stats(dolphin); + furi_record_close(RECORD_DOLPHIN); animation_manager->one_shot_view = one_shot_view_alloc(); one_shot_view_set_interact_callback( @@ -577,8 +580,19 @@ static void animation_manager_switch_to_one_shot_view(AnimationManager* animatio View* next_view = one_shot_view_get_view(animation_manager->one_shot_view); view_stack_remove_view(animation_manager->view_stack, prev_view); view_stack_add_view(animation_manager->view_stack, next_view); - one_shot_view_start_animation( - animation_manager->one_shot_view, XTREME_ASSETS()->A_Levelup_128x64); + if(XTREME_SETTINGS()->nsfw_mode) { + one_shot_view_start_animation(animation_manager->one_shot_view, &A_Levelup1_128x64); + } else { + if(stats.level <= 20) { + one_shot_view_start_animation( + animation_manager->one_shot_view, &A_Levelup1_128x64_sfw); + } else if(stats.level >= 21) { + one_shot_view_start_animation( + animation_manager->one_shot_view, &A_Levelup2_128x64_sfw); + } else { + furi_assert(0); + } + } } static void animation_manager_switch_to_animation_view(AnimationManager* animation_manager) { diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 4263dc0a4..a80a958a3 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -40,7 +40,7 @@ void animation_handler_select_manifest() { furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { - FURI_LOG_I(TAG, "Custom manifest selected"); + FURI_LOG_I(TAG, "Custom Manifest selected"); } else { use_asset_pack = false; } @@ -48,8 +48,14 @@ void animation_handler_select_manifest() { } if(!use_asset_pack) { furi_string_set(anim_dir, BASE_ANIMATION_DIR); + if(xtreme_settings->nsfw_mode) { + furi_string_cat_str(anim_dir, "/nsfw"); + FURI_LOG_I(TAG, "NSFW Manifest selected"); + } else { + furi_string_cat_str(anim_dir, "/sfw"); + FURI_LOG_I(TAG, "SFW Manifest selected"); + } furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); - FURI_LOG_I(TAG, "Base manifest selected"); } strlcpy(ANIMATION_DIR, furi_string_get_cstr(anim_dir), sizeof(ANIMATION_DIR)); strlcpy( diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index b5b73668b..7840cd00a 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -17,8 +17,6 @@ #include "helpers/pin_lock.h" #include "helpers/slideshow_filename.h" -#include "../../settings/xtreme_settings/xtreme_assets.h" - static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); static void desktop_start_auto_lock_timer(Desktop*); @@ -307,9 +305,6 @@ static bool desktop_check_file_flag(const char* flag_path) { int32_t desktop_srv(void* p) { UNUSED(p); - // TODO: find a (working) way to run this at startup without hooking desktop - XTREME_ASSETS_LOAD(); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { FURI_LOG_W("Desktop", "Desktop load skipped. Device is in special startup mode."); } else { diff --git a/applications/services/desktop/scenes/desktop_scene_fault.c b/applications/services/desktop/scenes/desktop_scene_fault.c index c2149253c..e4f5e788f 100644 --- a/applications/services/desktop/scenes/desktop_scene_fault.c +++ b/applications/services/desktop/scenes/desktop_scene_fault.c @@ -1,7 +1,7 @@ #include #include "../desktop_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" #define DesktopFaultEventExit 0x00FF00FF @@ -15,7 +15,7 @@ void desktop_scene_fault_on_enter(void* context) { Popup* popup = desktop->hw_mismatch_popup; popup_set_context(popup, desktop); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { popup_set_header( popup, "Slut passed out\n but is now back", diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index 450c5af23..f0430de5d 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -40,7 +40,7 @@ static void render_callback(Canvas* canvas, void* _ctx) { const char* mood_str = NULL; const Icon* portrait = NULL; - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { if(stats->butthurt <= 4) { portrait = xtreme_assets->I_passport_happy_46x49; mood_str = "Status: Wet"; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index 6fd26138b..ecab8c333 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -12,7 +12,7 @@ void power_settings_scene_power_off_on_enter(void* context) { DialogEx* dialog = app->dialog; dialog_ex_set_header(dialog, "Turn Off Device?", 64, 2, AlignCenter, AlignTop); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { dialog_ex_set_text( dialog, " I will be\nwaiting for\n you master", 78, 16, AlignLeft, AlignTop); } else { diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index ba5e65ca0..3323cf1df 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -3,6 +3,15 @@ #include #include +static void xtreme_settings_scene_start_base_graphics_changed(VariableItem* item) { + XtremeSettingsApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "NSFW" : "SFW"); + XTREME_SETTINGS()->nsfw_mode = value; + app->settings_changed = true; + app->assets_changed = true; +} + static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -163,6 +172,11 @@ void xtreme_settings_scene_start_on_enter(void* context) { storage_file_free(folder); furi_record_close(RECORD_STORAGE); + item = variable_item_list_add( + var_item_list, "Base Graphics", 2, xtreme_settings_scene_start_base_graphics_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->nsfw_mode); + variable_item_set_current_value_text(item, xtreme_settings->nsfw_mode ? "NSFW" : "SFW"); + item = variable_item_list_add( var_item_list, "Asset Pack", diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 13014b8d1..0f6ab998d 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -2,50 +2,175 @@ #include "assets_icons.h" #include -#define ICONS_FMT PACKS_DIR "/%s/Icons/%s" - XtremeAssets* xtreme_assets = NULL; -void anim(const Icon** replace, const char* name, FuriString* path, File* file) { - do { - furi_string_printf(path, ICONS_FMT "/meta", XTREME_SETTINGS()->asset_pack, name); - if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) - break; - int32_t width, height, frame_rate, frame_count; - storage_file_read(file, &width, 4); - storage_file_read(file, &height, 4); - storage_file_read(file, &frame_rate, 4); - storage_file_read(file, &frame_count, 4); - storage_file_close(file); - - Icon* icon = malloc(sizeof(Icon)); - FURI_CONST_ASSIGN(icon->width, width); - FURI_CONST_ASSIGN(icon->height, height); - FURI_CONST_ASSIGN(icon->frame_rate, frame_rate); - FURI_CONST_ASSIGN(icon->frame_count, frame_count); - icon->frames = malloc(sizeof(const uint8_t*) * icon->frame_count); - const char* pack = XTREME_SETTINGS()->asset_pack; - - bool ok = true; - for(int i = 0; ok && i < icon->frame_count; ++i) { - ok = false; - furi_string_printf(path, ICONS_FMT "/frame_%02d.bm", pack, name, i); - if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) - break; - - uint64_t size = storage_file_size(file); - FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(size)); - if(storage_file_read(file, (void*)icon->frames[i], size) == size) ok = true; - storage_file_close(file); - } - if(!ok) break; - - *replace = icon; - } while(false); +XtremeAssets* XTREME_ASSETS() { + if(xtreme_assets == NULL) { + XTREME_ASSETS_LOAD(); + } + return xtreme_assets; } -void icon(const Icon** replace, const char* name, FuriString* path, File* file) { - furi_string_printf(path, ICONS_FMT ".bmx", XTREME_SETTINGS()->asset_pack, name); +void XTREME_ASSETS_LOAD() { + if(xtreme_assets != NULL) return; + + xtreme_assets = malloc(sizeof(XtremeAssets)); + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + + if(xtreme_settings->nsfw_mode) { + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; + xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; + xtreme_assets->I_passport_bad_46x49 = &I_flipper; + xtreme_assets->I_passport_DB = &I_passport_DB; + xtreme_assets->I_passport_happy_46x49 = &I_flipper; + xtreme_assets->I_passport_okay_46x49 = &I_flipper; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; + xtreme_assets->I_Error_62x31 = &I_Error_62x31; + } else { + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw; + xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = + &I_iButtonDolphinVerySuccess_108x52_sfw; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw; + xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw; + xtreme_assets->I_passport_DB = &I_passport_DB_sfw; + xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw; + xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw; + xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw; + } + + if(xtreme_settings->asset_pack[0] == '\0') return; + FileInfo info; + FuriString* path = furi_string_alloc(); + const char* pack = xtreme_settings->asset_pack; + furi_string_printf(path, PACKS_DIR "/%s", pack); + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && + info.flags & FSF_DIRECTORY) { + File* file = storage_file_alloc(storage); + + swap_bmx_icon( + &xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_DolphinCommon_56x48, + pack, + "Dolphin/DolphinCommon_56x48.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinMafia_115x62, + pack, + "iButton/DolphinMafia_115x62.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_iButtonDolphinVerySuccess_108x52, + pack, + "iButton/iButtonDolphinVerySuccess_108x52.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinReadingSuccess_59x63, + pack, + "Infrared/DolphinReadingSuccess_59x63.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_NFC_dolphin_emulation_47x61, + pack, + "NFC/NFC_dolphin_emulation_47x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_passport_bad_46x49, + pack, + "Passport/passport_bad_46x49.bmx", + path, + file); + swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_passport_happy_46x49, + pack, + "Passport/passport_happy_46x49.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_passport_okay_46x49, + pack, + "Passport/passport_okay_46x49.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinReceive_97x61, + pack, + "RFID/RFIDDolphinReceive_97x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinSend_97x61, + pack, + "RFID/RFIDDolphinSend_97x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinSuccess_108x57, + pack, + "RFID/RFIDDolphinSuccess_108x57.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file); + swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file); + swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file); + + storage_file_free(file); + } + furi_record_close(RECORD_STORAGE); + furi_string_free(path); +} + +void swap_bmx_icon( + const Icon** replace, + const char* pack, + const char* name, + FuriString* path, + File* file) { + furi_string_printf(path, PACKS_DIR "/%s/Icons/%s", pack, name); if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t size = storage_file_size(file) - 8; int32_t width, height; @@ -65,80 +190,3 @@ void icon(const Icon** replace, const char* name, FuriString* path, File* file) storage_file_close(file); } } - -void swap(XtremeAssets* x, FuriString* p, File* f) { - anim(&x->A_Levelup_128x64, "Animations/Levelup_128x64", p, f); - icon(&x->I_BLE_Pairing_128x64, "BLE/BLE_Pairing_128x64", p, f); - icon(&x->I_DolphinCommon_56x48, "Dolphin/DolphinCommon_56x48", p, f); - icon(&x->I_DolphinMafia_115x62, "iButton/DolphinMafia_115x62", p, f); - icon(&x->I_DolphinNice_96x59, "iButton/DolphinNice_96x59", p, f); - icon(&x->I_DolphinWait_61x59, "iButton/DolphinWait_61x59", p, f); - icon(&x->I_iButtonDolphinVerySuccess_108x52, "iButton/iButtonDolphinVerySuccess_108x52", p, f); - icon(&x->I_DolphinReadingSuccess_59x63, "Infrared/DolphinReadingSuccess_59x63", p, f); - icon(&x->I_NFC_dolphin_emulation_47x61, "NFC/NFC_dolphin_emulation_47x61", p, f); - icon(&x->I_passport_bad_46x49, "Passport/passport_bad_46x49", p, f); - icon(&x->I_passport_DB, "Passport/passport_DB", p, f); - icon(&x->I_passport_happy_46x49, "Passport/passport_happy_46x49", p, f); - icon(&x->I_passport_okay_46x49, "Passport/passport_okay_46x49", p, f); - icon(&x->I_RFIDDolphinReceive_97x61, "RFID/RFIDDolphinReceive_97x61", p, f); - icon(&x->I_RFIDDolphinSend_97x61, "RFID/RFIDDolphinSend_97x61", p, f); - icon(&x->I_RFIDDolphinSuccess_108x57, "RFID/RFIDDolphinSuccess_108x57", p, f); - icon(&x->I_Cry_dolph_55x52, "Settings/Cry_dolph_55x52", p, f); - icon(&x->I_Scanning_123x52, "SubGhz/Scanning_123x52", p, f); - icon(&x->I_Auth_62x31, "U2F/Auth_62x31", p, f); - icon(&x->I_Connect_me_62x31, "U2F/Connect_me_62x31", p, f); - icon(&x->I_Connected_62x31, "U2F/Connected_62x31", p, f); - icon(&x->I_Error_62x31, "U2F/Error_62x31", p, f); -} - -void XTREME_ASSETS_LOAD() { - if(xtreme_assets != NULL) return; - - xtreme_assets = malloc(sizeof(XtremeAssets)); - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - - xtreme_assets->A_Levelup_128x64 = &A_Levelup_128x64; - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; - xtreme_assets->I_passport_bad_46x49 = &I_passport_bad_46x49; - xtreme_assets->I_passport_DB = &I_passport_DB; - xtreme_assets->I_passport_happy_46x49 = &I_passport_happy_46x49; - xtreme_assets->I_passport_okay_46x49 = &I_passport_okay_46x49; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; - xtreme_assets->I_Error_62x31 = &I_Error_62x31; - - if(xtreme_settings->asset_pack[0] == '\0') return; - xtreme_assets->is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; - FileInfo info; - FuriString* path = furi_string_alloc(); - furi_string_printf(path, PACKS_DIR "/%s", xtreme_settings->asset_pack); - Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && - info.flags & FSF_DIRECTORY) { - File* file = storage_file_alloc(storage); - swap(xtreme_assets, path, file); - storage_file_free(file); - } - furi_record_close(RECORD_STORAGE); - furi_string_free(path); -} - -XtremeAssets* XTREME_ASSETS() { - if(xtreme_assets == NULL) { - XTREME_ASSETS_LOAD(); - } - return xtreme_assets; -} diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index 038372a43..c49f5b590 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -7,8 +7,6 @@ #define PACKS_DIR EXT_PATH("dolphin_custom") typedef struct { - bool is_nsfw; - const Icon* A_Levelup_128x64; const Icon* I_BLE_Pairing_128x64; const Icon* I_DolphinCommon_56x48; const Icon* I_DolphinMafia_115x62; @@ -32,6 +30,15 @@ typedef struct { const Icon* I_Error_62x31; } XtremeAssets; +XtremeAssets* XTREME_ASSETS(); + void XTREME_ASSETS_LOAD(); -XtremeAssets* XTREME_ASSETS(); +void swap_bmx_icon( + const Icon** replace, + const char* base, + const char* name, + FuriString* path, + File* file); + +void free_bmx_icon(Icon* icon); diff --git a/applications/settings/xtreme_settings/xtreme_settings.h b/applications/settings/xtreme_settings/xtreme_settings.h index 13bb574ad..ee0ec5583 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.h +++ b/applications/settings/xtreme_settings/xtreme_settings.h @@ -18,6 +18,7 @@ typedef struct { int32_t cycle_anims; bool unlock_anims; + bool nsfw_mode; char asset_pack[MAX_PACK_NAME_LEN]; BatteryStyle battery_style; uint16_t anim_speed; diff --git a/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png b/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png deleted file mode 100644 index f60598005d41ff05a9f763f42f1a6b7900150e33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2610 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 Ud8X1kK>z>%07*qoM6N<$g8GWm`2YX_ diff --git a/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png b/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png deleted file mode 100644 index e80fea5bd7f694549da1b45e9f3db056342a95cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3376 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 diff --git a/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png deleted file mode 100644 index e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4224 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Μe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe diff --git a/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png b/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png deleted file mode 100644 index 98d7e15f9c105cc36c1f6d3473fd915a9687968b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6373 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Μe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png deleted file mode 100644 index 2528ebc95d79335975511a384c70c010d476a8c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4862 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 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png deleted file mode 100644 index fef503263fd962534e7e5543954612bf6c1faefd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4882 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 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png deleted file mode 100644 index 78c4e93f8a8c4e94eb0513c7b59f6dde68748205..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4466 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 Ef`SCamH+?% diff --git a/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png b/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png deleted file mode 100644 index d1164c59483a2242769327b5b2c9ac01acb83186..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3798 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 M07*qoM6N<$g8AuxBLDyZ diff --git a/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png b/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png deleted file mode 100644 index a48c5330e85c2135866fe99bcb6f3534d06c6d53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4092 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 diff --git a/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png b/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png deleted file mode 100644 index bc1010ca9da500055615c00359ea7536df4caa01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1874 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 Bfj=Do809goM&!5F$zl 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 Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png b/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png deleted file mode 100644 index 4beec55efbc4a6907e37e5d7422b9f7c33d3dbf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5122 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 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png deleted file mode 100644 index 90b589ff8e9f18b28d7fd75ca525a1db24381b06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4719 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 diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png b/assets/dolphin/external/nsfw/lvl_1/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png rename to assets/dolphin/external/nsfw/lvl_1/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png b/assets/dolphin/external/nsfw/lvl_1/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png rename to assets/dolphin/external/nsfw/lvl_1/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png b/assets/dolphin/external/nsfw/lvl_1/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png rename to assets/dolphin/external/nsfw/lvl_1/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png b/assets/dolphin/external/nsfw/lvl_1/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png rename to assets/dolphin/external/nsfw/lvl_1/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png b/assets/dolphin/external/nsfw/lvl_1/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png rename to assets/dolphin/external/nsfw/lvl_1/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png b/assets/dolphin/external/nsfw/lvl_1/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png rename to assets/dolphin/external/nsfw/lvl_1/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png b/assets/dolphin/external/nsfw/lvl_1/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png rename to assets/dolphin/external/nsfw/lvl_1/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png b/assets/dolphin/external/nsfw/lvl_1/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png rename to assets/dolphin/external/nsfw/lvl_1/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png b/assets/dolphin/external/nsfw/lvl_1/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png rename to assets/dolphin/external/nsfw/lvl_1/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png b/assets/dolphin/external/nsfw/lvl_1/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png rename to assets/dolphin/external/nsfw/lvl_1/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png b/assets/dolphin/external/nsfw/lvl_1/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png rename to assets/dolphin/external/nsfw/lvl_1/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png b/assets/dolphin/external/nsfw/lvl_1/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png rename to assets/dolphin/external/nsfw/lvl_1/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png b/assets/dolphin/external/nsfw/lvl_1/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png rename to assets/dolphin/external/nsfw/lvl_1/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png b/assets/dolphin/external/nsfw/lvl_1/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png rename to assets/dolphin/external/nsfw/lvl_1/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png b/assets/dolphin/external/nsfw/lvl_1/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png rename to assets/dolphin/external/nsfw/lvl_1/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png b/assets/dolphin/external/nsfw/lvl_1/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png rename to assets/dolphin/external/nsfw/lvl_1/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png b/assets/dolphin/external/nsfw/lvl_1/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png rename to assets/dolphin/external/nsfw/lvl_1/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png b/assets/dolphin/external/nsfw/lvl_1/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png rename to assets/dolphin/external/nsfw/lvl_1/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png b/assets/dolphin/external/nsfw/lvl_1/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png rename to assets/dolphin/external/nsfw/lvl_1/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png b/assets/dolphin/external/nsfw/lvl_1/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png rename to assets/dolphin/external/nsfw/lvl_1/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png b/assets/dolphin/external/nsfw/lvl_1/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png rename to assets/dolphin/external/nsfw/lvl_1/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png b/assets/dolphin/external/nsfw/lvl_1/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png rename to assets/dolphin/external/nsfw/lvl_1/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png b/assets/dolphin/external/nsfw/lvl_1/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png rename to assets/dolphin/external/nsfw/lvl_1/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png b/assets/dolphin/external/nsfw/lvl_1/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png rename to assets/dolphin/external/nsfw/lvl_1/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png b/assets/dolphin/external/nsfw/lvl_1/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png rename to assets/dolphin/external/nsfw/lvl_1/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png b/assets/dolphin/external/nsfw/lvl_1/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png rename to assets/dolphin/external/nsfw/lvl_1/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png b/assets/dolphin/external/nsfw/lvl_1/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png rename to assets/dolphin/external/nsfw/lvl_1/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png b/assets/dolphin/external/nsfw/lvl_1/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png rename to assets/dolphin/external/nsfw/lvl_1/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png b/assets/dolphin/external/nsfw/lvl_1/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png rename to assets/dolphin/external/nsfw/lvl_1/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png b/assets/dolphin/external/nsfw/lvl_1/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png rename to assets/dolphin/external/nsfw/lvl_1/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png b/assets/dolphin/external/nsfw/lvl_1/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png rename to assets/dolphin/external/nsfw/lvl_1/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt b/assets/dolphin/external/nsfw/lvl_1/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt rename to assets/dolphin/external/nsfw/lvl_1/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png b/assets/dolphin/external/nsfw/lvl_10/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png rename to assets/dolphin/external/nsfw/lvl_10/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png b/assets/dolphin/external/nsfw/lvl_10/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png rename to assets/dolphin/external/nsfw/lvl_10/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png b/assets/dolphin/external/nsfw/lvl_10/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png rename to assets/dolphin/external/nsfw/lvl_10/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png b/assets/dolphin/external/nsfw/lvl_10/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png rename to assets/dolphin/external/nsfw/lvl_10/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png b/assets/dolphin/external/nsfw/lvl_10/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png rename to assets/dolphin/external/nsfw/lvl_10/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png b/assets/dolphin/external/nsfw/lvl_10/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png rename to assets/dolphin/external/nsfw/lvl_10/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png b/assets/dolphin/external/nsfw/lvl_10/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png rename to assets/dolphin/external/nsfw/lvl_10/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png b/assets/dolphin/external/nsfw/lvl_10/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png rename to assets/dolphin/external/nsfw/lvl_10/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png b/assets/dolphin/external/nsfw/lvl_10/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png rename to assets/dolphin/external/nsfw/lvl_10/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png b/assets/dolphin/external/nsfw/lvl_10/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png rename to assets/dolphin/external/nsfw/lvl_10/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png b/assets/dolphin/external/nsfw/lvl_10/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png rename to assets/dolphin/external/nsfw/lvl_10/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png b/assets/dolphin/external/nsfw/lvl_10/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png rename to assets/dolphin/external/nsfw/lvl_10/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png b/assets/dolphin/external/nsfw/lvl_10/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png rename to assets/dolphin/external/nsfw/lvl_10/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png b/assets/dolphin/external/nsfw/lvl_10/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png rename to assets/dolphin/external/nsfw/lvl_10/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png b/assets/dolphin/external/nsfw/lvl_10/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png rename to assets/dolphin/external/nsfw/lvl_10/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png b/assets/dolphin/external/nsfw/lvl_10/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png rename to assets/dolphin/external/nsfw/lvl_10/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png b/assets/dolphin/external/nsfw/lvl_10/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png rename to assets/dolphin/external/nsfw/lvl_10/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png b/assets/dolphin/external/nsfw/lvl_10/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png rename to assets/dolphin/external/nsfw/lvl_10/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png b/assets/dolphin/external/nsfw/lvl_10/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png rename to assets/dolphin/external/nsfw/lvl_10/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png b/assets/dolphin/external/nsfw/lvl_10/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png rename to assets/dolphin/external/nsfw/lvl_10/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png b/assets/dolphin/external/nsfw/lvl_10/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png rename to assets/dolphin/external/nsfw/lvl_10/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png b/assets/dolphin/external/nsfw/lvl_10/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png rename to assets/dolphin/external/nsfw/lvl_10/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png b/assets/dolphin/external/nsfw/lvl_10/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png rename to assets/dolphin/external/nsfw/lvl_10/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png b/assets/dolphin/external/nsfw/lvl_10/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png rename to assets/dolphin/external/nsfw/lvl_10/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png b/assets/dolphin/external/nsfw/lvl_10/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png rename to assets/dolphin/external/nsfw/lvl_10/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png b/assets/dolphin/external/nsfw/lvl_10/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png rename to assets/dolphin/external/nsfw/lvl_10/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png b/assets/dolphin/external/nsfw/lvl_10/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png rename to assets/dolphin/external/nsfw/lvl_10/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png b/assets/dolphin/external/nsfw/lvl_10/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png rename to assets/dolphin/external/nsfw/lvl_10/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt b/assets/dolphin/external/nsfw/lvl_10/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt rename to assets/dolphin/external/nsfw/lvl_10/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png b/assets/dolphin/external/nsfw/lvl_11/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png rename to assets/dolphin/external/nsfw/lvl_11/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png b/assets/dolphin/external/nsfw/lvl_11/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png rename to assets/dolphin/external/nsfw/lvl_11/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png b/assets/dolphin/external/nsfw/lvl_11/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png rename to assets/dolphin/external/nsfw/lvl_11/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png b/assets/dolphin/external/nsfw/lvl_11/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png rename to assets/dolphin/external/nsfw/lvl_11/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png b/assets/dolphin/external/nsfw/lvl_11/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png rename to assets/dolphin/external/nsfw/lvl_11/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png b/assets/dolphin/external/nsfw/lvl_11/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png rename to assets/dolphin/external/nsfw/lvl_11/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png b/assets/dolphin/external/nsfw/lvl_11/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png rename to assets/dolphin/external/nsfw/lvl_11/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png b/assets/dolphin/external/nsfw/lvl_11/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png rename to assets/dolphin/external/nsfw/lvl_11/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png b/assets/dolphin/external/nsfw/lvl_11/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png rename to assets/dolphin/external/nsfw/lvl_11/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png b/assets/dolphin/external/nsfw/lvl_11/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png rename to assets/dolphin/external/nsfw/lvl_11/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png b/assets/dolphin/external/nsfw/lvl_11/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png rename to assets/dolphin/external/nsfw/lvl_11/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png b/assets/dolphin/external/nsfw/lvl_11/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png rename to assets/dolphin/external/nsfw/lvl_11/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png b/assets/dolphin/external/nsfw/lvl_11/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png rename to assets/dolphin/external/nsfw/lvl_11/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png b/assets/dolphin/external/nsfw/lvl_11/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png rename to assets/dolphin/external/nsfw/lvl_11/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png b/assets/dolphin/external/nsfw/lvl_11/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png rename to assets/dolphin/external/nsfw/lvl_11/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png b/assets/dolphin/external/nsfw/lvl_11/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png rename to assets/dolphin/external/nsfw/lvl_11/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png b/assets/dolphin/external/nsfw/lvl_11/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png rename to assets/dolphin/external/nsfw/lvl_11/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png b/assets/dolphin/external/nsfw/lvl_11/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png rename to assets/dolphin/external/nsfw/lvl_11/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png b/assets/dolphin/external/nsfw/lvl_11/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png rename to assets/dolphin/external/nsfw/lvl_11/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png b/assets/dolphin/external/nsfw/lvl_11/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png rename to assets/dolphin/external/nsfw/lvl_11/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png b/assets/dolphin/external/nsfw/lvl_11/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png rename to assets/dolphin/external/nsfw/lvl_11/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png b/assets/dolphin/external/nsfw/lvl_11/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png rename to assets/dolphin/external/nsfw/lvl_11/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png b/assets/dolphin/external/nsfw/lvl_11/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png rename to assets/dolphin/external/nsfw/lvl_11/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png b/assets/dolphin/external/nsfw/lvl_11/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png rename to assets/dolphin/external/nsfw/lvl_11/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png b/assets/dolphin/external/nsfw/lvl_11/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png rename to assets/dolphin/external/nsfw/lvl_11/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png b/assets/dolphin/external/nsfw/lvl_11/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png rename to assets/dolphin/external/nsfw/lvl_11/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png b/assets/dolphin/external/nsfw/lvl_11/frame_32.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png rename to assets/dolphin/external/nsfw/lvl_11/frame_32.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png b/assets/dolphin/external/nsfw/lvl_11/frame_33.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png rename to assets/dolphin/external/nsfw/lvl_11/frame_33.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png b/assets/dolphin/external/nsfw/lvl_11/frame_34.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png rename to assets/dolphin/external/nsfw/lvl_11/frame_34.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png b/assets/dolphin/external/nsfw/lvl_11/frame_35.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png rename to assets/dolphin/external/nsfw/lvl_11/frame_35.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png b/assets/dolphin/external/nsfw/lvl_11/frame_36.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png rename to assets/dolphin/external/nsfw/lvl_11/frame_36.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png b/assets/dolphin/external/nsfw/lvl_11/frame_37.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png rename to assets/dolphin/external/nsfw/lvl_11/frame_37.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png b/assets/dolphin/external/nsfw/lvl_11/frame_38.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png rename to assets/dolphin/external/nsfw/lvl_11/frame_38.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png b/assets/dolphin/external/nsfw/lvl_11/frame_39.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png rename to assets/dolphin/external/nsfw/lvl_11/frame_39.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png b/assets/dolphin/external/nsfw/lvl_11/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png rename to assets/dolphin/external/nsfw/lvl_11/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png b/assets/dolphin/external/nsfw/lvl_11/frame_40.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png rename to assets/dolphin/external/nsfw/lvl_11/frame_40.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png b/assets/dolphin/external/nsfw/lvl_11/frame_41.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png rename to assets/dolphin/external/nsfw/lvl_11/frame_41.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png b/assets/dolphin/external/nsfw/lvl_11/frame_42.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png rename to assets/dolphin/external/nsfw/lvl_11/frame_42.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png b/assets/dolphin/external/nsfw/lvl_11/frame_43.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png rename to assets/dolphin/external/nsfw/lvl_11/frame_43.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png b/assets/dolphin/external/nsfw/lvl_11/frame_44.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png rename to assets/dolphin/external/nsfw/lvl_11/frame_44.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png b/assets/dolphin/external/nsfw/lvl_11/frame_45.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png rename to assets/dolphin/external/nsfw/lvl_11/frame_45.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png b/assets/dolphin/external/nsfw/lvl_11/frame_46.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png rename to assets/dolphin/external/nsfw/lvl_11/frame_46.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png b/assets/dolphin/external/nsfw/lvl_11/frame_47.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png rename to assets/dolphin/external/nsfw/lvl_11/frame_47.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png b/assets/dolphin/external/nsfw/lvl_11/frame_48.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png rename to assets/dolphin/external/nsfw/lvl_11/frame_48.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png b/assets/dolphin/external/nsfw/lvl_11/frame_49.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png rename to assets/dolphin/external/nsfw/lvl_11/frame_49.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png b/assets/dolphin/external/nsfw/lvl_11/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png rename to assets/dolphin/external/nsfw/lvl_11/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png b/assets/dolphin/external/nsfw/lvl_11/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png rename to assets/dolphin/external/nsfw/lvl_11/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png b/assets/dolphin/external/nsfw/lvl_11/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png rename to assets/dolphin/external/nsfw/lvl_11/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png b/assets/dolphin/external/nsfw/lvl_11/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png rename to assets/dolphin/external/nsfw/lvl_11/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png b/assets/dolphin/external/nsfw/lvl_11/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png rename to assets/dolphin/external/nsfw/lvl_11/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt b/assets/dolphin/external/nsfw/lvl_11/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt rename to assets/dolphin/external/nsfw/lvl_11/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png b/assets/dolphin/external/nsfw/lvl_12/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png rename to assets/dolphin/external/nsfw/lvl_12/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png b/assets/dolphin/external/nsfw/lvl_12/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png rename to assets/dolphin/external/nsfw/lvl_12/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png b/assets/dolphin/external/nsfw/lvl_12/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png rename to assets/dolphin/external/nsfw/lvl_12/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png b/assets/dolphin/external/nsfw/lvl_12/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png rename to assets/dolphin/external/nsfw/lvl_12/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png b/assets/dolphin/external/nsfw/lvl_12/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png rename to assets/dolphin/external/nsfw/lvl_12/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png b/assets/dolphin/external/nsfw/lvl_12/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png rename to assets/dolphin/external/nsfw/lvl_12/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png b/assets/dolphin/external/nsfw/lvl_12/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png rename to assets/dolphin/external/nsfw/lvl_12/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png b/assets/dolphin/external/nsfw/lvl_12/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png rename to assets/dolphin/external/nsfw/lvl_12/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png b/assets/dolphin/external/nsfw/lvl_12/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png rename to assets/dolphin/external/nsfw/lvl_12/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png b/assets/dolphin/external/nsfw/lvl_12/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png rename to assets/dolphin/external/nsfw/lvl_12/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png b/assets/dolphin/external/nsfw/lvl_12/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png rename to assets/dolphin/external/nsfw/lvl_12/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png b/assets/dolphin/external/nsfw/lvl_12/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png rename to assets/dolphin/external/nsfw/lvl_12/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png b/assets/dolphin/external/nsfw/lvl_12/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png rename to assets/dolphin/external/nsfw/lvl_12/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png b/assets/dolphin/external/nsfw/lvl_12/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png rename to assets/dolphin/external/nsfw/lvl_12/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png b/assets/dolphin/external/nsfw/lvl_12/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png rename to assets/dolphin/external/nsfw/lvl_12/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png b/assets/dolphin/external/nsfw/lvl_12/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png rename to assets/dolphin/external/nsfw/lvl_12/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt b/assets/dolphin/external/nsfw/lvl_12/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt rename to assets/dolphin/external/nsfw/lvl_12/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png b/assets/dolphin/external/nsfw/lvl_13/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png rename to assets/dolphin/external/nsfw/lvl_13/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png b/assets/dolphin/external/nsfw/lvl_13/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png rename to assets/dolphin/external/nsfw/lvl_13/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png b/assets/dolphin/external/nsfw/lvl_13/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png rename to assets/dolphin/external/nsfw/lvl_13/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png b/assets/dolphin/external/nsfw/lvl_13/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png rename to assets/dolphin/external/nsfw/lvl_13/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png b/assets/dolphin/external/nsfw/lvl_13/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png rename to assets/dolphin/external/nsfw/lvl_13/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png b/assets/dolphin/external/nsfw/lvl_13/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png rename to assets/dolphin/external/nsfw/lvl_13/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png b/assets/dolphin/external/nsfw/lvl_13/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png rename to assets/dolphin/external/nsfw/lvl_13/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png b/assets/dolphin/external/nsfw/lvl_13/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png rename to assets/dolphin/external/nsfw/lvl_13/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png b/assets/dolphin/external/nsfw/lvl_13/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png rename to assets/dolphin/external/nsfw/lvl_13/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png b/assets/dolphin/external/nsfw/lvl_13/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png rename to assets/dolphin/external/nsfw/lvl_13/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png b/assets/dolphin/external/nsfw/lvl_13/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png rename to assets/dolphin/external/nsfw/lvl_13/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt b/assets/dolphin/external/nsfw/lvl_13/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt rename to assets/dolphin/external/nsfw/lvl_13/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png b/assets/dolphin/external/nsfw/lvl_14/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png rename to assets/dolphin/external/nsfw/lvl_14/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png b/assets/dolphin/external/nsfw/lvl_14/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png rename to assets/dolphin/external/nsfw/lvl_14/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png b/assets/dolphin/external/nsfw/lvl_14/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png rename to assets/dolphin/external/nsfw/lvl_14/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png b/assets/dolphin/external/nsfw/lvl_14/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png rename to assets/dolphin/external/nsfw/lvl_14/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png b/assets/dolphin/external/nsfw/lvl_14/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png rename to assets/dolphin/external/nsfw/lvl_14/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png b/assets/dolphin/external/nsfw/lvl_14/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png rename to assets/dolphin/external/nsfw/lvl_14/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png b/assets/dolphin/external/nsfw/lvl_14/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png rename to assets/dolphin/external/nsfw/lvl_14/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png b/assets/dolphin/external/nsfw/lvl_14/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png rename to assets/dolphin/external/nsfw/lvl_14/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt b/assets/dolphin/external/nsfw/lvl_14/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt rename to assets/dolphin/external/nsfw/lvl_14/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png b/assets/dolphin/external/nsfw/lvl_15/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png rename to assets/dolphin/external/nsfw/lvl_15/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png b/assets/dolphin/external/nsfw/lvl_15/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png rename to assets/dolphin/external/nsfw/lvl_15/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png b/assets/dolphin/external/nsfw/lvl_15/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png rename to assets/dolphin/external/nsfw/lvl_15/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png b/assets/dolphin/external/nsfw/lvl_15/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png rename to assets/dolphin/external/nsfw/lvl_15/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png b/assets/dolphin/external/nsfw/lvl_15/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png rename to assets/dolphin/external/nsfw/lvl_15/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png b/assets/dolphin/external/nsfw/lvl_15/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png rename to assets/dolphin/external/nsfw/lvl_15/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png b/assets/dolphin/external/nsfw/lvl_15/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png rename to assets/dolphin/external/nsfw/lvl_15/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png b/assets/dolphin/external/nsfw/lvl_15/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png rename to assets/dolphin/external/nsfw/lvl_15/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png b/assets/dolphin/external/nsfw/lvl_15/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png rename to assets/dolphin/external/nsfw/lvl_15/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png b/assets/dolphin/external/nsfw/lvl_15/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png rename to assets/dolphin/external/nsfw/lvl_15/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png b/assets/dolphin/external/nsfw/lvl_15/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png rename to assets/dolphin/external/nsfw/lvl_15/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png b/assets/dolphin/external/nsfw/lvl_15/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png rename to assets/dolphin/external/nsfw/lvl_15/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png b/assets/dolphin/external/nsfw/lvl_15/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png rename to assets/dolphin/external/nsfw/lvl_15/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png b/assets/dolphin/external/nsfw/lvl_15/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png rename to assets/dolphin/external/nsfw/lvl_15/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png b/assets/dolphin/external/nsfw/lvl_15/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png rename to assets/dolphin/external/nsfw/lvl_15/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png b/assets/dolphin/external/nsfw/lvl_15/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png rename to assets/dolphin/external/nsfw/lvl_15/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png b/assets/dolphin/external/nsfw/lvl_15/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png rename to assets/dolphin/external/nsfw/lvl_15/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png b/assets/dolphin/external/nsfw/lvl_15/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png rename to assets/dolphin/external/nsfw/lvl_15/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png b/assets/dolphin/external/nsfw/lvl_15/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png rename to assets/dolphin/external/nsfw/lvl_15/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png b/assets/dolphin/external/nsfw/lvl_15/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png rename to assets/dolphin/external/nsfw/lvl_15/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png b/assets/dolphin/external/nsfw/lvl_15/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png rename to assets/dolphin/external/nsfw/lvl_15/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png b/assets/dolphin/external/nsfw/lvl_15/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png rename to assets/dolphin/external/nsfw/lvl_15/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt b/assets/dolphin/external/nsfw/lvl_15/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt rename to assets/dolphin/external/nsfw/lvl_15/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png b/assets/dolphin/external/nsfw/lvl_16/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png rename to assets/dolphin/external/nsfw/lvl_16/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png b/assets/dolphin/external/nsfw/lvl_16/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png rename to assets/dolphin/external/nsfw/lvl_16/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png b/assets/dolphin/external/nsfw/lvl_16/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png rename to assets/dolphin/external/nsfw/lvl_16/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png b/assets/dolphin/external/nsfw/lvl_16/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png rename to assets/dolphin/external/nsfw/lvl_16/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png b/assets/dolphin/external/nsfw/lvl_16/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png rename to assets/dolphin/external/nsfw/lvl_16/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png b/assets/dolphin/external/nsfw/lvl_16/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png rename to assets/dolphin/external/nsfw/lvl_16/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png b/assets/dolphin/external/nsfw/lvl_16/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png rename to assets/dolphin/external/nsfw/lvl_16/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png b/assets/dolphin/external/nsfw/lvl_16/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png rename to assets/dolphin/external/nsfw/lvl_16/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png b/assets/dolphin/external/nsfw/lvl_16/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png rename to assets/dolphin/external/nsfw/lvl_16/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png b/assets/dolphin/external/nsfw/lvl_16/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png rename to assets/dolphin/external/nsfw/lvl_16/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png b/assets/dolphin/external/nsfw/lvl_16/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png rename to assets/dolphin/external/nsfw/lvl_16/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png b/assets/dolphin/external/nsfw/lvl_16/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png rename to assets/dolphin/external/nsfw/lvl_16/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png b/assets/dolphin/external/nsfw/lvl_16/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png rename to assets/dolphin/external/nsfw/lvl_16/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png b/assets/dolphin/external/nsfw/lvl_16/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png rename to assets/dolphin/external/nsfw/lvl_16/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png b/assets/dolphin/external/nsfw/lvl_16/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png rename to assets/dolphin/external/nsfw/lvl_16/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png b/assets/dolphin/external/nsfw/lvl_16/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png rename to assets/dolphin/external/nsfw/lvl_16/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png b/assets/dolphin/external/nsfw/lvl_16/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png rename to assets/dolphin/external/nsfw/lvl_16/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png b/assets/dolphin/external/nsfw/lvl_16/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png rename to assets/dolphin/external/nsfw/lvl_16/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png b/assets/dolphin/external/nsfw/lvl_16/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png rename to assets/dolphin/external/nsfw/lvl_16/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png b/assets/dolphin/external/nsfw/lvl_16/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png rename to assets/dolphin/external/nsfw/lvl_16/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png b/assets/dolphin/external/nsfw/lvl_16/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png rename to assets/dolphin/external/nsfw/lvl_16/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt b/assets/dolphin/external/nsfw/lvl_16/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt rename to assets/dolphin/external/nsfw/lvl_16/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png b/assets/dolphin/external/nsfw/lvl_17/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png rename to assets/dolphin/external/nsfw/lvl_17/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png b/assets/dolphin/external/nsfw/lvl_17/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png rename to assets/dolphin/external/nsfw/lvl_17/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png b/assets/dolphin/external/nsfw/lvl_17/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png rename to assets/dolphin/external/nsfw/lvl_17/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png b/assets/dolphin/external/nsfw/lvl_17/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png rename to assets/dolphin/external/nsfw/lvl_17/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png b/assets/dolphin/external/nsfw/lvl_17/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png rename to assets/dolphin/external/nsfw/lvl_17/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png b/assets/dolphin/external/nsfw/lvl_17/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png rename to assets/dolphin/external/nsfw/lvl_17/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png b/assets/dolphin/external/nsfw/lvl_17/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png rename to assets/dolphin/external/nsfw/lvl_17/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png b/assets/dolphin/external/nsfw/lvl_17/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png rename to assets/dolphin/external/nsfw/lvl_17/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png b/assets/dolphin/external/nsfw/lvl_17/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png rename to assets/dolphin/external/nsfw/lvl_17/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png b/assets/dolphin/external/nsfw/lvl_17/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png rename to assets/dolphin/external/nsfw/lvl_17/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png b/assets/dolphin/external/nsfw/lvl_17/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png rename to assets/dolphin/external/nsfw/lvl_17/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png b/assets/dolphin/external/nsfw/lvl_17/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png rename to assets/dolphin/external/nsfw/lvl_17/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png b/assets/dolphin/external/nsfw/lvl_17/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png rename to assets/dolphin/external/nsfw/lvl_17/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png b/assets/dolphin/external/nsfw/lvl_17/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png rename to assets/dolphin/external/nsfw/lvl_17/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png b/assets/dolphin/external/nsfw/lvl_17/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png rename to assets/dolphin/external/nsfw/lvl_17/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png b/assets/dolphin/external/nsfw/lvl_17/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png rename to assets/dolphin/external/nsfw/lvl_17/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png b/assets/dolphin/external/nsfw/lvl_17/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png rename to assets/dolphin/external/nsfw/lvl_17/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png b/assets/dolphin/external/nsfw/lvl_17/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png rename to assets/dolphin/external/nsfw/lvl_17/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png b/assets/dolphin/external/nsfw/lvl_17/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png rename to assets/dolphin/external/nsfw/lvl_17/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png b/assets/dolphin/external/nsfw/lvl_17/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png rename to assets/dolphin/external/nsfw/lvl_17/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png b/assets/dolphin/external/nsfw/lvl_17/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png rename to assets/dolphin/external/nsfw/lvl_17/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png b/assets/dolphin/external/nsfw/lvl_17/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png rename to assets/dolphin/external/nsfw/lvl_17/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png b/assets/dolphin/external/nsfw/lvl_17/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png rename to assets/dolphin/external/nsfw/lvl_17/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png b/assets/dolphin/external/nsfw/lvl_17/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png rename to assets/dolphin/external/nsfw/lvl_17/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png b/assets/dolphin/external/nsfw/lvl_17/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png rename to assets/dolphin/external/nsfw/lvl_17/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png b/assets/dolphin/external/nsfw/lvl_17/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png rename to assets/dolphin/external/nsfw/lvl_17/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png b/assets/dolphin/external/nsfw/lvl_17/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png rename to assets/dolphin/external/nsfw/lvl_17/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png b/assets/dolphin/external/nsfw/lvl_17/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png rename to assets/dolphin/external/nsfw/lvl_17/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png b/assets/dolphin/external/nsfw/lvl_17/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png rename to assets/dolphin/external/nsfw/lvl_17/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png b/assets/dolphin/external/nsfw/lvl_17/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png rename to assets/dolphin/external/nsfw/lvl_17/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png b/assets/dolphin/external/nsfw/lvl_17/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png rename to assets/dolphin/external/nsfw/lvl_17/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png b/assets/dolphin/external/nsfw/lvl_17/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png rename to assets/dolphin/external/nsfw/lvl_17/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt b/assets/dolphin/external/nsfw/lvl_17/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt rename to assets/dolphin/external/nsfw/lvl_17/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png b/assets/dolphin/external/nsfw/lvl_18/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png rename to assets/dolphin/external/nsfw/lvl_18/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png b/assets/dolphin/external/nsfw/lvl_18/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png rename to assets/dolphin/external/nsfw/lvl_18/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png b/assets/dolphin/external/nsfw/lvl_18/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png rename to assets/dolphin/external/nsfw/lvl_18/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png b/assets/dolphin/external/nsfw/lvl_18/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png rename to assets/dolphin/external/nsfw/lvl_18/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png b/assets/dolphin/external/nsfw/lvl_18/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png rename to assets/dolphin/external/nsfw/lvl_18/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png b/assets/dolphin/external/nsfw/lvl_18/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png rename to assets/dolphin/external/nsfw/lvl_18/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png b/assets/dolphin/external/nsfw/lvl_18/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png rename to assets/dolphin/external/nsfw/lvl_18/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png b/assets/dolphin/external/nsfw/lvl_18/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png rename to assets/dolphin/external/nsfw/lvl_18/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png b/assets/dolphin/external/nsfw/lvl_18/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png rename to assets/dolphin/external/nsfw/lvl_18/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png b/assets/dolphin/external/nsfw/lvl_18/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png rename to assets/dolphin/external/nsfw/lvl_18/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png b/assets/dolphin/external/nsfw/lvl_18/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png rename to assets/dolphin/external/nsfw/lvl_18/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png b/assets/dolphin/external/nsfw/lvl_18/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png rename to assets/dolphin/external/nsfw/lvl_18/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png b/assets/dolphin/external/nsfw/lvl_18/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png rename to assets/dolphin/external/nsfw/lvl_18/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png b/assets/dolphin/external/nsfw/lvl_18/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png rename to assets/dolphin/external/nsfw/lvl_18/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png b/assets/dolphin/external/nsfw/lvl_18/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png rename to assets/dolphin/external/nsfw/lvl_18/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png b/assets/dolphin/external/nsfw/lvl_18/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png rename to assets/dolphin/external/nsfw/lvl_18/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png b/assets/dolphin/external/nsfw/lvl_18/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png rename to assets/dolphin/external/nsfw/lvl_18/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png b/assets/dolphin/external/nsfw/lvl_18/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png rename to assets/dolphin/external/nsfw/lvl_18/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png b/assets/dolphin/external/nsfw/lvl_18/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png rename to assets/dolphin/external/nsfw/lvl_18/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png b/assets/dolphin/external/nsfw/lvl_18/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png rename to assets/dolphin/external/nsfw/lvl_18/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png b/assets/dolphin/external/nsfw/lvl_18/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png rename to assets/dolphin/external/nsfw/lvl_18/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png b/assets/dolphin/external/nsfw/lvl_18/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png rename to assets/dolphin/external/nsfw/lvl_18/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png b/assets/dolphin/external/nsfw/lvl_18/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png rename to assets/dolphin/external/nsfw/lvl_18/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt b/assets/dolphin/external/nsfw/lvl_18/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt rename to assets/dolphin/external/nsfw/lvl_18/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png b/assets/dolphin/external/nsfw/lvl_19/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png rename to assets/dolphin/external/nsfw/lvl_19/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png b/assets/dolphin/external/nsfw/lvl_19/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png rename to assets/dolphin/external/nsfw/lvl_19/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png b/assets/dolphin/external/nsfw/lvl_19/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png rename to assets/dolphin/external/nsfw/lvl_19/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png b/assets/dolphin/external/nsfw/lvl_19/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png rename to assets/dolphin/external/nsfw/lvl_19/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png b/assets/dolphin/external/nsfw/lvl_19/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png rename to assets/dolphin/external/nsfw/lvl_19/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png b/assets/dolphin/external/nsfw/lvl_19/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png rename to assets/dolphin/external/nsfw/lvl_19/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png b/assets/dolphin/external/nsfw/lvl_19/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png rename to assets/dolphin/external/nsfw/lvl_19/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png b/assets/dolphin/external/nsfw/lvl_19/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png rename to assets/dolphin/external/nsfw/lvl_19/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png b/assets/dolphin/external/nsfw/lvl_19/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png rename to assets/dolphin/external/nsfw/lvl_19/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png b/assets/dolphin/external/nsfw/lvl_19/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png rename to assets/dolphin/external/nsfw/lvl_19/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png b/assets/dolphin/external/nsfw/lvl_19/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png rename to assets/dolphin/external/nsfw/lvl_19/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png b/assets/dolphin/external/nsfw/lvl_19/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png rename to assets/dolphin/external/nsfw/lvl_19/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png b/assets/dolphin/external/nsfw/lvl_19/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png rename to assets/dolphin/external/nsfw/lvl_19/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png b/assets/dolphin/external/nsfw/lvl_19/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png rename to assets/dolphin/external/nsfw/lvl_19/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png b/assets/dolphin/external/nsfw/lvl_19/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png rename to assets/dolphin/external/nsfw/lvl_19/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png b/assets/dolphin/external/nsfw/lvl_19/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png rename to assets/dolphin/external/nsfw/lvl_19/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png b/assets/dolphin/external/nsfw/lvl_19/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png rename to assets/dolphin/external/nsfw/lvl_19/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png b/assets/dolphin/external/nsfw/lvl_19/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png rename to assets/dolphin/external/nsfw/lvl_19/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png b/assets/dolphin/external/nsfw/lvl_19/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png rename to assets/dolphin/external/nsfw/lvl_19/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png b/assets/dolphin/external/nsfw/lvl_19/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png rename to assets/dolphin/external/nsfw/lvl_19/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png b/assets/dolphin/external/nsfw/lvl_19/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png rename to assets/dolphin/external/nsfw/lvl_19/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png b/assets/dolphin/external/nsfw/lvl_19/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png rename to assets/dolphin/external/nsfw/lvl_19/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt b/assets/dolphin/external/nsfw/lvl_19/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt rename to assets/dolphin/external/nsfw/lvl_19/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png b/assets/dolphin/external/nsfw/lvl_2/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png rename to assets/dolphin/external/nsfw/lvl_2/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png b/assets/dolphin/external/nsfw/lvl_2/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png rename to assets/dolphin/external/nsfw/lvl_2/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png b/assets/dolphin/external/nsfw/lvl_2/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png rename to assets/dolphin/external/nsfw/lvl_2/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png b/assets/dolphin/external/nsfw/lvl_2/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png rename to assets/dolphin/external/nsfw/lvl_2/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png b/assets/dolphin/external/nsfw/lvl_2/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png rename to assets/dolphin/external/nsfw/lvl_2/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png b/assets/dolphin/external/nsfw/lvl_2/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png rename to assets/dolphin/external/nsfw/lvl_2/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png b/assets/dolphin/external/nsfw/lvl_2/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png rename to assets/dolphin/external/nsfw/lvl_2/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png b/assets/dolphin/external/nsfw/lvl_2/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png rename to assets/dolphin/external/nsfw/lvl_2/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png b/assets/dolphin/external/nsfw/lvl_2/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png rename to assets/dolphin/external/nsfw/lvl_2/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png b/assets/dolphin/external/nsfw/lvl_2/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png rename to assets/dolphin/external/nsfw/lvl_2/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png b/assets/dolphin/external/nsfw/lvl_2/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png rename to assets/dolphin/external/nsfw/lvl_2/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png b/assets/dolphin/external/nsfw/lvl_2/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png rename to assets/dolphin/external/nsfw/lvl_2/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png b/assets/dolphin/external/nsfw/lvl_2/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png rename to assets/dolphin/external/nsfw/lvl_2/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png b/assets/dolphin/external/nsfw/lvl_2/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png rename to assets/dolphin/external/nsfw/lvl_2/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png b/assets/dolphin/external/nsfw/lvl_2/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png rename to assets/dolphin/external/nsfw/lvl_2/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt b/assets/dolphin/external/nsfw/lvl_2/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt rename to assets/dolphin/external/nsfw/lvl_2/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png b/assets/dolphin/external/nsfw/lvl_20/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png rename to assets/dolphin/external/nsfw/lvl_20/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png b/assets/dolphin/external/nsfw/lvl_20/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png rename to assets/dolphin/external/nsfw/lvl_20/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png b/assets/dolphin/external/nsfw/lvl_20/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png rename to assets/dolphin/external/nsfw/lvl_20/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png b/assets/dolphin/external/nsfw/lvl_20/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png rename to assets/dolphin/external/nsfw/lvl_20/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png b/assets/dolphin/external/nsfw/lvl_20/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png rename to assets/dolphin/external/nsfw/lvl_20/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png b/assets/dolphin/external/nsfw/lvl_20/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png rename to assets/dolphin/external/nsfw/lvl_20/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png b/assets/dolphin/external/nsfw/lvl_20/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png rename to assets/dolphin/external/nsfw/lvl_20/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png b/assets/dolphin/external/nsfw/lvl_20/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png rename to assets/dolphin/external/nsfw/lvl_20/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png b/assets/dolphin/external/nsfw/lvl_20/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png rename to assets/dolphin/external/nsfw/lvl_20/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png b/assets/dolphin/external/nsfw/lvl_20/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png rename to assets/dolphin/external/nsfw/lvl_20/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png b/assets/dolphin/external/nsfw/lvl_20/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png rename to assets/dolphin/external/nsfw/lvl_20/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png b/assets/dolphin/external/nsfw/lvl_20/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png rename to assets/dolphin/external/nsfw/lvl_20/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png b/assets/dolphin/external/nsfw/lvl_20/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png rename to assets/dolphin/external/nsfw/lvl_20/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png b/assets/dolphin/external/nsfw/lvl_20/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png rename to assets/dolphin/external/nsfw/lvl_20/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt b/assets/dolphin/external/nsfw/lvl_20/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt rename to assets/dolphin/external/nsfw/lvl_20/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png b/assets/dolphin/external/nsfw/lvl_21/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png rename to assets/dolphin/external/nsfw/lvl_21/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png b/assets/dolphin/external/nsfw/lvl_21/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png rename to assets/dolphin/external/nsfw/lvl_21/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png b/assets/dolphin/external/nsfw/lvl_21/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png rename to assets/dolphin/external/nsfw/lvl_21/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png b/assets/dolphin/external/nsfw/lvl_21/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png rename to assets/dolphin/external/nsfw/lvl_21/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png b/assets/dolphin/external/nsfw/lvl_21/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png rename to assets/dolphin/external/nsfw/lvl_21/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png b/assets/dolphin/external/nsfw/lvl_21/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png rename to assets/dolphin/external/nsfw/lvl_21/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt b/assets/dolphin/external/nsfw/lvl_21/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt rename to assets/dolphin/external/nsfw/lvl_21/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png b/assets/dolphin/external/nsfw/lvl_22/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png rename to assets/dolphin/external/nsfw/lvl_22/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png b/assets/dolphin/external/nsfw/lvl_22/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png rename to assets/dolphin/external/nsfw/lvl_22/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png b/assets/dolphin/external/nsfw/lvl_22/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png rename to assets/dolphin/external/nsfw/lvl_22/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png b/assets/dolphin/external/nsfw/lvl_22/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png rename to assets/dolphin/external/nsfw/lvl_22/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png b/assets/dolphin/external/nsfw/lvl_22/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png rename to assets/dolphin/external/nsfw/lvl_22/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png b/assets/dolphin/external/nsfw/lvl_22/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png rename to assets/dolphin/external/nsfw/lvl_22/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png b/assets/dolphin/external/nsfw/lvl_22/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png rename to assets/dolphin/external/nsfw/lvl_22/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png b/assets/dolphin/external/nsfw/lvl_22/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png rename to assets/dolphin/external/nsfw/lvl_22/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png b/assets/dolphin/external/nsfw/lvl_22/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png rename to assets/dolphin/external/nsfw/lvl_22/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png b/assets/dolphin/external/nsfw/lvl_22/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png rename to assets/dolphin/external/nsfw/lvl_22/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png b/assets/dolphin/external/nsfw/lvl_22/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png rename to assets/dolphin/external/nsfw/lvl_22/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png b/assets/dolphin/external/nsfw/lvl_22/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png rename to assets/dolphin/external/nsfw/lvl_22/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png b/assets/dolphin/external/nsfw/lvl_22/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png rename to assets/dolphin/external/nsfw/lvl_22/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png b/assets/dolphin/external/nsfw/lvl_22/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png rename to assets/dolphin/external/nsfw/lvl_22/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png b/assets/dolphin/external/nsfw/lvl_22/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png rename to assets/dolphin/external/nsfw/lvl_22/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png b/assets/dolphin/external/nsfw/lvl_22/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png rename to assets/dolphin/external/nsfw/lvl_22/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png b/assets/dolphin/external/nsfw/lvl_22/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png rename to assets/dolphin/external/nsfw/lvl_22/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png b/assets/dolphin/external/nsfw/lvl_22/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png rename to assets/dolphin/external/nsfw/lvl_22/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png b/assets/dolphin/external/nsfw/lvl_22/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png rename to assets/dolphin/external/nsfw/lvl_22/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png b/assets/dolphin/external/nsfw/lvl_22/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png rename to assets/dolphin/external/nsfw/lvl_22/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png b/assets/dolphin/external/nsfw/lvl_22/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png rename to assets/dolphin/external/nsfw/lvl_22/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png b/assets/dolphin/external/nsfw/lvl_22/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png rename to assets/dolphin/external/nsfw/lvl_22/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png b/assets/dolphin/external/nsfw/lvl_22/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png rename to assets/dolphin/external/nsfw/lvl_22/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png b/assets/dolphin/external/nsfw/lvl_22/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png rename to assets/dolphin/external/nsfw/lvl_22/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png b/assets/dolphin/external/nsfw/lvl_22/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png rename to assets/dolphin/external/nsfw/lvl_22/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png b/assets/dolphin/external/nsfw/lvl_22/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png rename to assets/dolphin/external/nsfw/lvl_22/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png b/assets/dolphin/external/nsfw/lvl_22/frame_32.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png rename to assets/dolphin/external/nsfw/lvl_22/frame_32.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png b/assets/dolphin/external/nsfw/lvl_22/frame_33.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png rename to assets/dolphin/external/nsfw/lvl_22/frame_33.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png b/assets/dolphin/external/nsfw/lvl_22/frame_34.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png rename to assets/dolphin/external/nsfw/lvl_22/frame_34.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png b/assets/dolphin/external/nsfw/lvl_22/frame_35.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png rename to assets/dolphin/external/nsfw/lvl_22/frame_35.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png b/assets/dolphin/external/nsfw/lvl_22/frame_36.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png rename to assets/dolphin/external/nsfw/lvl_22/frame_36.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png b/assets/dolphin/external/nsfw/lvl_22/frame_37.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png rename to assets/dolphin/external/nsfw/lvl_22/frame_37.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png b/assets/dolphin/external/nsfw/lvl_22/frame_38.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png rename to assets/dolphin/external/nsfw/lvl_22/frame_38.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png b/assets/dolphin/external/nsfw/lvl_22/frame_39.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png rename to assets/dolphin/external/nsfw/lvl_22/frame_39.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png b/assets/dolphin/external/nsfw/lvl_22/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png rename to assets/dolphin/external/nsfw/lvl_22/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png b/assets/dolphin/external/nsfw/lvl_22/frame_40.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png rename to assets/dolphin/external/nsfw/lvl_22/frame_40.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png b/assets/dolphin/external/nsfw/lvl_22/frame_41.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png rename to assets/dolphin/external/nsfw/lvl_22/frame_41.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png b/assets/dolphin/external/nsfw/lvl_22/frame_42.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png rename to assets/dolphin/external/nsfw/lvl_22/frame_42.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png b/assets/dolphin/external/nsfw/lvl_22/frame_43.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png rename to assets/dolphin/external/nsfw/lvl_22/frame_43.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png b/assets/dolphin/external/nsfw/lvl_22/frame_44.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png rename to assets/dolphin/external/nsfw/lvl_22/frame_44.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png b/assets/dolphin/external/nsfw/lvl_22/frame_45.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png rename to assets/dolphin/external/nsfw/lvl_22/frame_45.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png b/assets/dolphin/external/nsfw/lvl_22/frame_46.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png rename to assets/dolphin/external/nsfw/lvl_22/frame_46.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png b/assets/dolphin/external/nsfw/lvl_22/frame_47.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png rename to assets/dolphin/external/nsfw/lvl_22/frame_47.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png b/assets/dolphin/external/nsfw/lvl_22/frame_48.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png rename to assets/dolphin/external/nsfw/lvl_22/frame_48.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png b/assets/dolphin/external/nsfw/lvl_22/frame_49.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png rename to assets/dolphin/external/nsfw/lvl_22/frame_49.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png b/assets/dolphin/external/nsfw/lvl_22/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png rename to assets/dolphin/external/nsfw/lvl_22/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png b/assets/dolphin/external/nsfw/lvl_22/frame_50.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png rename to assets/dolphin/external/nsfw/lvl_22/frame_50.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png b/assets/dolphin/external/nsfw/lvl_22/frame_51.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png rename to assets/dolphin/external/nsfw/lvl_22/frame_51.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png b/assets/dolphin/external/nsfw/lvl_22/frame_52.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png rename to assets/dolphin/external/nsfw/lvl_22/frame_52.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png b/assets/dolphin/external/nsfw/lvl_22/frame_53.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png rename to assets/dolphin/external/nsfw/lvl_22/frame_53.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png b/assets/dolphin/external/nsfw/lvl_22/frame_54.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png rename to assets/dolphin/external/nsfw/lvl_22/frame_54.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png b/assets/dolphin/external/nsfw/lvl_22/frame_55.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png rename to assets/dolphin/external/nsfw/lvl_22/frame_55.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png b/assets/dolphin/external/nsfw/lvl_22/frame_56.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png rename to assets/dolphin/external/nsfw/lvl_22/frame_56.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png b/assets/dolphin/external/nsfw/lvl_22/frame_57.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png rename to assets/dolphin/external/nsfw/lvl_22/frame_57.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png b/assets/dolphin/external/nsfw/lvl_22/frame_58.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png rename to assets/dolphin/external/nsfw/lvl_22/frame_58.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png b/assets/dolphin/external/nsfw/lvl_22/frame_59.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png rename to assets/dolphin/external/nsfw/lvl_22/frame_59.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png b/assets/dolphin/external/nsfw/lvl_22/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png rename to assets/dolphin/external/nsfw/lvl_22/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png b/assets/dolphin/external/nsfw/lvl_22/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png rename to assets/dolphin/external/nsfw/lvl_22/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png b/assets/dolphin/external/nsfw/lvl_22/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png rename to assets/dolphin/external/nsfw/lvl_22/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png b/assets/dolphin/external/nsfw/lvl_22/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png rename to assets/dolphin/external/nsfw/lvl_22/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt b/assets/dolphin/external/nsfw/lvl_22/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt rename to assets/dolphin/external/nsfw/lvl_22/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png b/assets/dolphin/external/nsfw/lvl_23/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png rename to assets/dolphin/external/nsfw/lvl_23/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png b/assets/dolphin/external/nsfw/lvl_23/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png rename to assets/dolphin/external/nsfw/lvl_23/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png b/assets/dolphin/external/nsfw/lvl_23/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png rename to assets/dolphin/external/nsfw/lvl_23/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png b/assets/dolphin/external/nsfw/lvl_23/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png rename to assets/dolphin/external/nsfw/lvl_23/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png b/assets/dolphin/external/nsfw/lvl_23/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png rename to assets/dolphin/external/nsfw/lvl_23/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png b/assets/dolphin/external/nsfw/lvl_23/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png rename to assets/dolphin/external/nsfw/lvl_23/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png b/assets/dolphin/external/nsfw/lvl_23/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png rename to assets/dolphin/external/nsfw/lvl_23/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png b/assets/dolphin/external/nsfw/lvl_23/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png rename to assets/dolphin/external/nsfw/lvl_23/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png b/assets/dolphin/external/nsfw/lvl_23/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png rename to assets/dolphin/external/nsfw/lvl_23/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png b/assets/dolphin/external/nsfw/lvl_23/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png rename to assets/dolphin/external/nsfw/lvl_23/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png b/assets/dolphin/external/nsfw/lvl_23/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png rename to assets/dolphin/external/nsfw/lvl_23/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png b/assets/dolphin/external/nsfw/lvl_23/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png rename to assets/dolphin/external/nsfw/lvl_23/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png b/assets/dolphin/external/nsfw/lvl_23/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png rename to assets/dolphin/external/nsfw/lvl_23/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png b/assets/dolphin/external/nsfw/lvl_23/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png rename to assets/dolphin/external/nsfw/lvl_23/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png b/assets/dolphin/external/nsfw/lvl_23/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png rename to assets/dolphin/external/nsfw/lvl_23/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png b/assets/dolphin/external/nsfw/lvl_23/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png rename to assets/dolphin/external/nsfw/lvl_23/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png b/assets/dolphin/external/nsfw/lvl_23/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png rename to assets/dolphin/external/nsfw/lvl_23/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt b/assets/dolphin/external/nsfw/lvl_23/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt rename to assets/dolphin/external/nsfw/lvl_23/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png b/assets/dolphin/external/nsfw/lvl_24/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png rename to assets/dolphin/external/nsfw/lvl_24/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png b/assets/dolphin/external/nsfw/lvl_24/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png rename to assets/dolphin/external/nsfw/lvl_24/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png b/assets/dolphin/external/nsfw/lvl_24/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png rename to assets/dolphin/external/nsfw/lvl_24/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png b/assets/dolphin/external/nsfw/lvl_24/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png rename to assets/dolphin/external/nsfw/lvl_24/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png b/assets/dolphin/external/nsfw/lvl_24/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png rename to assets/dolphin/external/nsfw/lvl_24/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png b/assets/dolphin/external/nsfw/lvl_24/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png rename to assets/dolphin/external/nsfw/lvl_24/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png b/assets/dolphin/external/nsfw/lvl_24/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png rename to assets/dolphin/external/nsfw/lvl_24/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png b/assets/dolphin/external/nsfw/lvl_24/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png rename to assets/dolphin/external/nsfw/lvl_24/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png b/assets/dolphin/external/nsfw/lvl_24/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png rename to assets/dolphin/external/nsfw/lvl_24/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png b/assets/dolphin/external/nsfw/lvl_24/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png rename to assets/dolphin/external/nsfw/lvl_24/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png b/assets/dolphin/external/nsfw/lvl_24/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png rename to assets/dolphin/external/nsfw/lvl_24/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png b/assets/dolphin/external/nsfw/lvl_24/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png rename to assets/dolphin/external/nsfw/lvl_24/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png b/assets/dolphin/external/nsfw/lvl_24/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png rename to assets/dolphin/external/nsfw/lvl_24/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png b/assets/dolphin/external/nsfw/lvl_24/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png rename to assets/dolphin/external/nsfw/lvl_24/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png b/assets/dolphin/external/nsfw/lvl_24/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png rename to assets/dolphin/external/nsfw/lvl_24/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png b/assets/dolphin/external/nsfw/lvl_24/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png rename to assets/dolphin/external/nsfw/lvl_24/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png b/assets/dolphin/external/nsfw/lvl_24/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png rename to assets/dolphin/external/nsfw/lvl_24/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png b/assets/dolphin/external/nsfw/lvl_24/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png rename to assets/dolphin/external/nsfw/lvl_24/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png b/assets/dolphin/external/nsfw/lvl_24/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png rename to assets/dolphin/external/nsfw/lvl_24/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png b/assets/dolphin/external/nsfw/lvl_24/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png rename to assets/dolphin/external/nsfw/lvl_24/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png b/assets/dolphin/external/nsfw/lvl_24/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png rename to assets/dolphin/external/nsfw/lvl_24/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png b/assets/dolphin/external/nsfw/lvl_24/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png rename to assets/dolphin/external/nsfw/lvl_24/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png b/assets/dolphin/external/nsfw/lvl_24/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png rename to assets/dolphin/external/nsfw/lvl_24/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png b/assets/dolphin/external/nsfw/lvl_24/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png rename to assets/dolphin/external/nsfw/lvl_24/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png b/assets/dolphin/external/nsfw/lvl_24/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png rename to assets/dolphin/external/nsfw/lvl_24/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png b/assets/dolphin/external/nsfw/lvl_24/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png rename to assets/dolphin/external/nsfw/lvl_24/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png b/assets/dolphin/external/nsfw/lvl_24/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png rename to assets/dolphin/external/nsfw/lvl_24/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png b/assets/dolphin/external/nsfw/lvl_24/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png rename to assets/dolphin/external/nsfw/lvl_24/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png b/assets/dolphin/external/nsfw/lvl_24/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png rename to assets/dolphin/external/nsfw/lvl_24/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png b/assets/dolphin/external/nsfw/lvl_24/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png rename to assets/dolphin/external/nsfw/lvl_24/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt b/assets/dolphin/external/nsfw/lvl_24/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt rename to assets/dolphin/external/nsfw/lvl_24/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png b/assets/dolphin/external/nsfw/lvl_25/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png rename to assets/dolphin/external/nsfw/lvl_25/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png b/assets/dolphin/external/nsfw/lvl_25/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png rename to assets/dolphin/external/nsfw/lvl_25/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png b/assets/dolphin/external/nsfw/lvl_25/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png rename to assets/dolphin/external/nsfw/lvl_25/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png b/assets/dolphin/external/nsfw/lvl_25/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png rename to assets/dolphin/external/nsfw/lvl_25/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png b/assets/dolphin/external/nsfw/lvl_25/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png rename to assets/dolphin/external/nsfw/lvl_25/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png b/assets/dolphin/external/nsfw/lvl_25/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png rename to assets/dolphin/external/nsfw/lvl_25/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png b/assets/dolphin/external/nsfw/lvl_25/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png rename to assets/dolphin/external/nsfw/lvl_25/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png b/assets/dolphin/external/nsfw/lvl_25/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png rename to assets/dolphin/external/nsfw/lvl_25/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png b/assets/dolphin/external/nsfw/lvl_25/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png rename to assets/dolphin/external/nsfw/lvl_25/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png b/assets/dolphin/external/nsfw/lvl_25/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png rename to assets/dolphin/external/nsfw/lvl_25/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png b/assets/dolphin/external/nsfw/lvl_25/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png rename to assets/dolphin/external/nsfw/lvl_25/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png b/assets/dolphin/external/nsfw/lvl_25/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png rename to assets/dolphin/external/nsfw/lvl_25/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png b/assets/dolphin/external/nsfw/lvl_25/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png rename to assets/dolphin/external/nsfw/lvl_25/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png b/assets/dolphin/external/nsfw/lvl_25/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png rename to assets/dolphin/external/nsfw/lvl_25/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png b/assets/dolphin/external/nsfw/lvl_25/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png rename to assets/dolphin/external/nsfw/lvl_25/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png b/assets/dolphin/external/nsfw/lvl_25/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png rename to assets/dolphin/external/nsfw/lvl_25/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png b/assets/dolphin/external/nsfw/lvl_25/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png rename to assets/dolphin/external/nsfw/lvl_25/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png b/assets/dolphin/external/nsfw/lvl_25/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png rename to assets/dolphin/external/nsfw/lvl_25/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png b/assets/dolphin/external/nsfw/lvl_25/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png rename to assets/dolphin/external/nsfw/lvl_25/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png b/assets/dolphin/external/nsfw/lvl_25/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png rename to assets/dolphin/external/nsfw/lvl_25/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png b/assets/dolphin/external/nsfw/lvl_25/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png rename to assets/dolphin/external/nsfw/lvl_25/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png b/assets/dolphin/external/nsfw/lvl_25/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png rename to assets/dolphin/external/nsfw/lvl_25/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png b/assets/dolphin/external/nsfw/lvl_25/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png rename to assets/dolphin/external/nsfw/lvl_25/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png b/assets/dolphin/external/nsfw/lvl_25/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png rename to assets/dolphin/external/nsfw/lvl_25/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png b/assets/dolphin/external/nsfw/lvl_25/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png rename to assets/dolphin/external/nsfw/lvl_25/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png b/assets/dolphin/external/nsfw/lvl_25/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png rename to assets/dolphin/external/nsfw/lvl_25/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png b/assets/dolphin/external/nsfw/lvl_25/frame_32.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png rename to assets/dolphin/external/nsfw/lvl_25/frame_32.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png b/assets/dolphin/external/nsfw/lvl_25/frame_33.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png rename to assets/dolphin/external/nsfw/lvl_25/frame_33.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png b/assets/dolphin/external/nsfw/lvl_25/frame_34.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png rename to assets/dolphin/external/nsfw/lvl_25/frame_34.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png b/assets/dolphin/external/nsfw/lvl_25/frame_35.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png rename to assets/dolphin/external/nsfw/lvl_25/frame_35.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png b/assets/dolphin/external/nsfw/lvl_25/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png rename to assets/dolphin/external/nsfw/lvl_25/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png b/assets/dolphin/external/nsfw/lvl_25/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png rename to assets/dolphin/external/nsfw/lvl_25/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png b/assets/dolphin/external/nsfw/lvl_25/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png rename to assets/dolphin/external/nsfw/lvl_25/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png b/assets/dolphin/external/nsfw/lvl_25/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png rename to assets/dolphin/external/nsfw/lvl_25/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png b/assets/dolphin/external/nsfw/lvl_25/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png rename to assets/dolphin/external/nsfw/lvl_25/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png b/assets/dolphin/external/nsfw/lvl_25/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png rename to assets/dolphin/external/nsfw/lvl_25/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt b/assets/dolphin/external/nsfw/lvl_25/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt rename to assets/dolphin/external/nsfw/lvl_25/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png b/assets/dolphin/external/nsfw/lvl_26/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png rename to assets/dolphin/external/nsfw/lvl_26/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png b/assets/dolphin/external/nsfw/lvl_26/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png rename to assets/dolphin/external/nsfw/lvl_26/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png b/assets/dolphin/external/nsfw/lvl_26/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png rename to assets/dolphin/external/nsfw/lvl_26/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png b/assets/dolphin/external/nsfw/lvl_26/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png rename to assets/dolphin/external/nsfw/lvl_26/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png b/assets/dolphin/external/nsfw/lvl_26/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png rename to assets/dolphin/external/nsfw/lvl_26/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png b/assets/dolphin/external/nsfw/lvl_26/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png rename to assets/dolphin/external/nsfw/lvl_26/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png b/assets/dolphin/external/nsfw/lvl_26/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png rename to assets/dolphin/external/nsfw/lvl_26/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png b/assets/dolphin/external/nsfw/lvl_26/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png rename to assets/dolphin/external/nsfw/lvl_26/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png b/assets/dolphin/external/nsfw/lvl_26/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png rename to assets/dolphin/external/nsfw/lvl_26/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png b/assets/dolphin/external/nsfw/lvl_26/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png rename to assets/dolphin/external/nsfw/lvl_26/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png b/assets/dolphin/external/nsfw/lvl_26/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png rename to assets/dolphin/external/nsfw/lvl_26/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png b/assets/dolphin/external/nsfw/lvl_26/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png rename to assets/dolphin/external/nsfw/lvl_26/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt b/assets/dolphin/external/nsfw/lvl_26/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt rename to assets/dolphin/external/nsfw/lvl_26/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png b/assets/dolphin/external/nsfw/lvl_27/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png rename to assets/dolphin/external/nsfw/lvl_27/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png b/assets/dolphin/external/nsfw/lvl_27/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png rename to assets/dolphin/external/nsfw/lvl_27/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png b/assets/dolphin/external/nsfw/lvl_27/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png rename to assets/dolphin/external/nsfw/lvl_27/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png b/assets/dolphin/external/nsfw/lvl_27/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png rename to assets/dolphin/external/nsfw/lvl_27/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png b/assets/dolphin/external/nsfw/lvl_27/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png rename to assets/dolphin/external/nsfw/lvl_27/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png b/assets/dolphin/external/nsfw/lvl_27/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png rename to assets/dolphin/external/nsfw/lvl_27/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png b/assets/dolphin/external/nsfw/lvl_27/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png rename to assets/dolphin/external/nsfw/lvl_27/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png b/assets/dolphin/external/nsfw/lvl_27/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png rename to assets/dolphin/external/nsfw/lvl_27/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png b/assets/dolphin/external/nsfw/lvl_27/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png rename to assets/dolphin/external/nsfw/lvl_27/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png b/assets/dolphin/external/nsfw/lvl_27/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png rename to assets/dolphin/external/nsfw/lvl_27/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png b/assets/dolphin/external/nsfw/lvl_27/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png rename to assets/dolphin/external/nsfw/lvl_27/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png b/assets/dolphin/external/nsfw/lvl_27/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png rename to assets/dolphin/external/nsfw/lvl_27/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png b/assets/dolphin/external/nsfw/lvl_27/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png rename to assets/dolphin/external/nsfw/lvl_27/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png b/assets/dolphin/external/nsfw/lvl_27/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png rename to assets/dolphin/external/nsfw/lvl_27/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png b/assets/dolphin/external/nsfw/lvl_27/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png rename to assets/dolphin/external/nsfw/lvl_27/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png b/assets/dolphin/external/nsfw/lvl_27/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png rename to assets/dolphin/external/nsfw/lvl_27/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png b/assets/dolphin/external/nsfw/lvl_27/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png rename to assets/dolphin/external/nsfw/lvl_27/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png b/assets/dolphin/external/nsfw/lvl_27/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png rename to assets/dolphin/external/nsfw/lvl_27/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png b/assets/dolphin/external/nsfw/lvl_27/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png rename to assets/dolphin/external/nsfw/lvl_27/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png b/assets/dolphin/external/nsfw/lvl_27/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png rename to assets/dolphin/external/nsfw/lvl_27/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png b/assets/dolphin/external/nsfw/lvl_27/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png rename to assets/dolphin/external/nsfw/lvl_27/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png b/assets/dolphin/external/nsfw/lvl_27/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png rename to assets/dolphin/external/nsfw/lvl_27/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt b/assets/dolphin/external/nsfw/lvl_27/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt rename to assets/dolphin/external/nsfw/lvl_27/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png b/assets/dolphin/external/nsfw/lvl_28/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png rename to assets/dolphin/external/nsfw/lvl_28/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png b/assets/dolphin/external/nsfw/lvl_28/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png rename to assets/dolphin/external/nsfw/lvl_28/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png b/assets/dolphin/external/nsfw/lvl_28/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png rename to assets/dolphin/external/nsfw/lvl_28/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png b/assets/dolphin/external/nsfw/lvl_28/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png rename to assets/dolphin/external/nsfw/lvl_28/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png b/assets/dolphin/external/nsfw/lvl_28/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png rename to assets/dolphin/external/nsfw/lvl_28/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png b/assets/dolphin/external/nsfw/lvl_28/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png rename to assets/dolphin/external/nsfw/lvl_28/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt b/assets/dolphin/external/nsfw/lvl_28/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt rename to assets/dolphin/external/nsfw/lvl_28/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png b/assets/dolphin/external/nsfw/lvl_29/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png rename to assets/dolphin/external/nsfw/lvl_29/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png b/assets/dolphin/external/nsfw/lvl_29/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png rename to assets/dolphin/external/nsfw/lvl_29/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png b/assets/dolphin/external/nsfw/lvl_29/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png rename to assets/dolphin/external/nsfw/lvl_29/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png b/assets/dolphin/external/nsfw/lvl_29/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png rename to assets/dolphin/external/nsfw/lvl_29/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png b/assets/dolphin/external/nsfw/lvl_29/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png rename to assets/dolphin/external/nsfw/lvl_29/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png b/assets/dolphin/external/nsfw/lvl_29/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png rename to assets/dolphin/external/nsfw/lvl_29/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png b/assets/dolphin/external/nsfw/lvl_29/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png rename to assets/dolphin/external/nsfw/lvl_29/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png b/assets/dolphin/external/nsfw/lvl_29/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png rename to assets/dolphin/external/nsfw/lvl_29/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png b/assets/dolphin/external/nsfw/lvl_29/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png rename to assets/dolphin/external/nsfw/lvl_29/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png b/assets/dolphin/external/nsfw/lvl_29/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png rename to assets/dolphin/external/nsfw/lvl_29/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png b/assets/dolphin/external/nsfw/lvl_29/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png rename to assets/dolphin/external/nsfw/lvl_29/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png b/assets/dolphin/external/nsfw/lvl_29/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png rename to assets/dolphin/external/nsfw/lvl_29/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png b/assets/dolphin/external/nsfw/lvl_29/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png rename to assets/dolphin/external/nsfw/lvl_29/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png b/assets/dolphin/external/nsfw/lvl_29/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png rename to assets/dolphin/external/nsfw/lvl_29/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png b/assets/dolphin/external/nsfw/lvl_29/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png rename to assets/dolphin/external/nsfw/lvl_29/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png b/assets/dolphin/external/nsfw/lvl_29/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png rename to assets/dolphin/external/nsfw/lvl_29/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png b/assets/dolphin/external/nsfw/lvl_29/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png rename to assets/dolphin/external/nsfw/lvl_29/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png b/assets/dolphin/external/nsfw/lvl_29/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png rename to assets/dolphin/external/nsfw/lvl_29/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png b/assets/dolphin/external/nsfw/lvl_29/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png rename to assets/dolphin/external/nsfw/lvl_29/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png b/assets/dolphin/external/nsfw/lvl_29/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png rename to assets/dolphin/external/nsfw/lvl_29/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png b/assets/dolphin/external/nsfw/lvl_29/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png rename to assets/dolphin/external/nsfw/lvl_29/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png b/assets/dolphin/external/nsfw/lvl_29/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png rename to assets/dolphin/external/nsfw/lvl_29/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png b/assets/dolphin/external/nsfw/lvl_29/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png rename to assets/dolphin/external/nsfw/lvl_29/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png b/assets/dolphin/external/nsfw/lvl_29/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png rename to assets/dolphin/external/nsfw/lvl_29/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png b/assets/dolphin/external/nsfw/lvl_29/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png rename to assets/dolphin/external/nsfw/lvl_29/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png b/assets/dolphin/external/nsfw/lvl_29/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png rename to assets/dolphin/external/nsfw/lvl_29/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png b/assets/dolphin/external/nsfw/lvl_29/frame_32.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png rename to assets/dolphin/external/nsfw/lvl_29/frame_32.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png b/assets/dolphin/external/nsfw/lvl_29/frame_33.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png rename to assets/dolphin/external/nsfw/lvl_29/frame_33.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png b/assets/dolphin/external/nsfw/lvl_29/frame_34.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png rename to assets/dolphin/external/nsfw/lvl_29/frame_34.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png b/assets/dolphin/external/nsfw/lvl_29/frame_35.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png rename to assets/dolphin/external/nsfw/lvl_29/frame_35.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png b/assets/dolphin/external/nsfw/lvl_29/frame_36.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png rename to assets/dolphin/external/nsfw/lvl_29/frame_36.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png b/assets/dolphin/external/nsfw/lvl_29/frame_37.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png rename to assets/dolphin/external/nsfw/lvl_29/frame_37.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png b/assets/dolphin/external/nsfw/lvl_29/frame_38.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png rename to assets/dolphin/external/nsfw/lvl_29/frame_38.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png b/assets/dolphin/external/nsfw/lvl_29/frame_39.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png rename to assets/dolphin/external/nsfw/lvl_29/frame_39.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png b/assets/dolphin/external/nsfw/lvl_29/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png rename to assets/dolphin/external/nsfw/lvl_29/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png b/assets/dolphin/external/nsfw/lvl_29/frame_40.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png rename to assets/dolphin/external/nsfw/lvl_29/frame_40.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png b/assets/dolphin/external/nsfw/lvl_29/frame_41.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png rename to assets/dolphin/external/nsfw/lvl_29/frame_41.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png b/assets/dolphin/external/nsfw/lvl_29/frame_42.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png rename to assets/dolphin/external/nsfw/lvl_29/frame_42.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png b/assets/dolphin/external/nsfw/lvl_29/frame_43.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png rename to assets/dolphin/external/nsfw/lvl_29/frame_43.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png b/assets/dolphin/external/nsfw/lvl_29/frame_44.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png rename to assets/dolphin/external/nsfw/lvl_29/frame_44.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png b/assets/dolphin/external/nsfw/lvl_29/frame_45.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png rename to assets/dolphin/external/nsfw/lvl_29/frame_45.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png b/assets/dolphin/external/nsfw/lvl_29/frame_46.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png rename to assets/dolphin/external/nsfw/lvl_29/frame_46.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png b/assets/dolphin/external/nsfw/lvl_29/frame_47.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png rename to assets/dolphin/external/nsfw/lvl_29/frame_47.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png b/assets/dolphin/external/nsfw/lvl_29/frame_48.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png rename to assets/dolphin/external/nsfw/lvl_29/frame_48.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png b/assets/dolphin/external/nsfw/lvl_29/frame_49.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png rename to assets/dolphin/external/nsfw/lvl_29/frame_49.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png b/assets/dolphin/external/nsfw/lvl_29/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png rename to assets/dolphin/external/nsfw/lvl_29/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png b/assets/dolphin/external/nsfw/lvl_29/frame_50.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png rename to assets/dolphin/external/nsfw/lvl_29/frame_50.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png b/assets/dolphin/external/nsfw/lvl_29/frame_51.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png rename to assets/dolphin/external/nsfw/lvl_29/frame_51.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png b/assets/dolphin/external/nsfw/lvl_29/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png rename to assets/dolphin/external/nsfw/lvl_29/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png b/assets/dolphin/external/nsfw/lvl_29/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png rename to assets/dolphin/external/nsfw/lvl_29/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png b/assets/dolphin/external/nsfw/lvl_29/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png rename to assets/dolphin/external/nsfw/lvl_29/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png b/assets/dolphin/external/nsfw/lvl_29/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png rename to assets/dolphin/external/nsfw/lvl_29/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt b/assets/dolphin/external/nsfw/lvl_29/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt rename to assets/dolphin/external/nsfw/lvl_29/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png b/assets/dolphin/external/nsfw/lvl_3/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png rename to assets/dolphin/external/nsfw/lvl_3/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png b/assets/dolphin/external/nsfw/lvl_3/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png rename to assets/dolphin/external/nsfw/lvl_3/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png b/assets/dolphin/external/nsfw/lvl_3/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png rename to assets/dolphin/external/nsfw/lvl_3/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png b/assets/dolphin/external/nsfw/lvl_3/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png rename to assets/dolphin/external/nsfw/lvl_3/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png b/assets/dolphin/external/nsfw/lvl_3/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png rename to assets/dolphin/external/nsfw/lvl_3/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png b/assets/dolphin/external/nsfw/lvl_3/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png rename to assets/dolphin/external/nsfw/lvl_3/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png b/assets/dolphin/external/nsfw/lvl_3/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png rename to assets/dolphin/external/nsfw/lvl_3/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png b/assets/dolphin/external/nsfw/lvl_3/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png rename to assets/dolphin/external/nsfw/lvl_3/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png b/assets/dolphin/external/nsfw/lvl_3/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png rename to assets/dolphin/external/nsfw/lvl_3/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png b/assets/dolphin/external/nsfw/lvl_3/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png rename to assets/dolphin/external/nsfw/lvl_3/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png b/assets/dolphin/external/nsfw/lvl_3/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png rename to assets/dolphin/external/nsfw/lvl_3/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png b/assets/dolphin/external/nsfw/lvl_3/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png rename to assets/dolphin/external/nsfw/lvl_3/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png b/assets/dolphin/external/nsfw/lvl_3/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png rename to assets/dolphin/external/nsfw/lvl_3/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png b/assets/dolphin/external/nsfw/lvl_3/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png rename to assets/dolphin/external/nsfw/lvl_3/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png b/assets/dolphin/external/nsfw/lvl_3/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png rename to assets/dolphin/external/nsfw/lvl_3/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt b/assets/dolphin/external/nsfw/lvl_3/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt rename to assets/dolphin/external/nsfw/lvl_3/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png b/assets/dolphin/external/nsfw/lvl_30/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png rename to assets/dolphin/external/nsfw/lvl_30/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png b/assets/dolphin/external/nsfw/lvl_30/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png rename to assets/dolphin/external/nsfw/lvl_30/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png b/assets/dolphin/external/nsfw/lvl_30/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png rename to assets/dolphin/external/nsfw/lvl_30/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png b/assets/dolphin/external/nsfw/lvl_30/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png rename to assets/dolphin/external/nsfw/lvl_30/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png b/assets/dolphin/external/nsfw/lvl_30/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png rename to assets/dolphin/external/nsfw/lvl_30/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png b/assets/dolphin/external/nsfw/lvl_30/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png rename to assets/dolphin/external/nsfw/lvl_30/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png b/assets/dolphin/external/nsfw/lvl_30/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png rename to assets/dolphin/external/nsfw/lvl_30/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png b/assets/dolphin/external/nsfw/lvl_30/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png rename to assets/dolphin/external/nsfw/lvl_30/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png b/assets/dolphin/external/nsfw/lvl_30/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png rename to assets/dolphin/external/nsfw/lvl_30/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png b/assets/dolphin/external/nsfw/lvl_30/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png rename to assets/dolphin/external/nsfw/lvl_30/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png b/assets/dolphin/external/nsfw/lvl_30/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png rename to assets/dolphin/external/nsfw/lvl_30/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png b/assets/dolphin/external/nsfw/lvl_30/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png rename to assets/dolphin/external/nsfw/lvl_30/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png b/assets/dolphin/external/nsfw/lvl_30/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png rename to assets/dolphin/external/nsfw/lvl_30/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png b/assets/dolphin/external/nsfw/lvl_30/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png rename to assets/dolphin/external/nsfw/lvl_30/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png b/assets/dolphin/external/nsfw/lvl_30/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png rename to assets/dolphin/external/nsfw/lvl_30/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png b/assets/dolphin/external/nsfw/lvl_30/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png rename to assets/dolphin/external/nsfw/lvl_30/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png b/assets/dolphin/external/nsfw/lvl_30/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png rename to assets/dolphin/external/nsfw/lvl_30/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png b/assets/dolphin/external/nsfw/lvl_30/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png rename to assets/dolphin/external/nsfw/lvl_30/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png b/assets/dolphin/external/nsfw/lvl_30/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png rename to assets/dolphin/external/nsfw/lvl_30/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png b/assets/dolphin/external/nsfw/lvl_30/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png rename to assets/dolphin/external/nsfw/lvl_30/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png b/assets/dolphin/external/nsfw/lvl_30/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png rename to assets/dolphin/external/nsfw/lvl_30/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png b/assets/dolphin/external/nsfw/lvl_30/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png rename to assets/dolphin/external/nsfw/lvl_30/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png b/assets/dolphin/external/nsfw/lvl_30/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png rename to assets/dolphin/external/nsfw/lvl_30/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png b/assets/dolphin/external/nsfw/lvl_30/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png rename to assets/dolphin/external/nsfw/lvl_30/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png b/assets/dolphin/external/nsfw/lvl_30/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png rename to assets/dolphin/external/nsfw/lvl_30/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png b/assets/dolphin/external/nsfw/lvl_30/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png rename to assets/dolphin/external/nsfw/lvl_30/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png b/assets/dolphin/external/nsfw/lvl_30/frame_32.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png rename to assets/dolphin/external/nsfw/lvl_30/frame_32.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png b/assets/dolphin/external/nsfw/lvl_30/frame_33.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png rename to assets/dolphin/external/nsfw/lvl_30/frame_33.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png b/assets/dolphin/external/nsfw/lvl_30/frame_34.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png rename to assets/dolphin/external/nsfw/lvl_30/frame_34.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png b/assets/dolphin/external/nsfw/lvl_30/frame_35.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png rename to assets/dolphin/external/nsfw/lvl_30/frame_35.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png b/assets/dolphin/external/nsfw/lvl_30/frame_36.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png rename to assets/dolphin/external/nsfw/lvl_30/frame_36.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png b/assets/dolphin/external/nsfw/lvl_30/frame_37.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png rename to assets/dolphin/external/nsfw/lvl_30/frame_37.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png b/assets/dolphin/external/nsfw/lvl_30/frame_38.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png rename to assets/dolphin/external/nsfw/lvl_30/frame_38.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png b/assets/dolphin/external/nsfw/lvl_30/frame_39.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png rename to assets/dolphin/external/nsfw/lvl_30/frame_39.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png b/assets/dolphin/external/nsfw/lvl_30/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png rename to assets/dolphin/external/nsfw/lvl_30/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png b/assets/dolphin/external/nsfw/lvl_30/frame_40.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png rename to assets/dolphin/external/nsfw/lvl_30/frame_40.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png b/assets/dolphin/external/nsfw/lvl_30/frame_41.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png rename to assets/dolphin/external/nsfw/lvl_30/frame_41.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png b/assets/dolphin/external/nsfw/lvl_30/frame_42.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png rename to assets/dolphin/external/nsfw/lvl_30/frame_42.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png b/assets/dolphin/external/nsfw/lvl_30/frame_43.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png rename to assets/dolphin/external/nsfw/lvl_30/frame_43.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png b/assets/dolphin/external/nsfw/lvl_30/frame_44.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png rename to assets/dolphin/external/nsfw/lvl_30/frame_44.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png b/assets/dolphin/external/nsfw/lvl_30/frame_45.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png rename to assets/dolphin/external/nsfw/lvl_30/frame_45.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png b/assets/dolphin/external/nsfw/lvl_30/frame_46.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png rename to assets/dolphin/external/nsfw/lvl_30/frame_46.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png b/assets/dolphin/external/nsfw/lvl_30/frame_47.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png rename to assets/dolphin/external/nsfw/lvl_30/frame_47.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png b/assets/dolphin/external/nsfw/lvl_30/frame_48.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png rename to assets/dolphin/external/nsfw/lvl_30/frame_48.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png b/assets/dolphin/external/nsfw/lvl_30/frame_49.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png rename to assets/dolphin/external/nsfw/lvl_30/frame_49.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png b/assets/dolphin/external/nsfw/lvl_30/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png rename to assets/dolphin/external/nsfw/lvl_30/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png b/assets/dolphin/external/nsfw/lvl_30/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png rename to assets/dolphin/external/nsfw/lvl_30/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png b/assets/dolphin/external/nsfw/lvl_30/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png rename to assets/dolphin/external/nsfw/lvl_30/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png b/assets/dolphin/external/nsfw/lvl_30/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png rename to assets/dolphin/external/nsfw/lvl_30/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png b/assets/dolphin/external/nsfw/lvl_30/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png rename to assets/dolphin/external/nsfw/lvl_30/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt b/assets/dolphin/external/nsfw/lvl_30/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt rename to assets/dolphin/external/nsfw/lvl_30/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png b/assets/dolphin/external/nsfw/lvl_4/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png rename to assets/dolphin/external/nsfw/lvl_4/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png b/assets/dolphin/external/nsfw/lvl_4/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png rename to assets/dolphin/external/nsfw/lvl_4/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png b/assets/dolphin/external/nsfw/lvl_4/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png rename to assets/dolphin/external/nsfw/lvl_4/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png b/assets/dolphin/external/nsfw/lvl_4/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png rename to assets/dolphin/external/nsfw/lvl_4/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png b/assets/dolphin/external/nsfw/lvl_4/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png rename to assets/dolphin/external/nsfw/lvl_4/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png b/assets/dolphin/external/nsfw/lvl_4/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png rename to assets/dolphin/external/nsfw/lvl_4/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png b/assets/dolphin/external/nsfw/lvl_4/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png rename to assets/dolphin/external/nsfw/lvl_4/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png b/assets/dolphin/external/nsfw/lvl_4/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png rename to assets/dolphin/external/nsfw/lvl_4/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png b/assets/dolphin/external/nsfw/lvl_4/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png rename to assets/dolphin/external/nsfw/lvl_4/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png b/assets/dolphin/external/nsfw/lvl_4/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png rename to assets/dolphin/external/nsfw/lvl_4/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png b/assets/dolphin/external/nsfw/lvl_4/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png rename to assets/dolphin/external/nsfw/lvl_4/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png b/assets/dolphin/external/nsfw/lvl_4/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png rename to assets/dolphin/external/nsfw/lvl_4/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png b/assets/dolphin/external/nsfw/lvl_4/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png rename to assets/dolphin/external/nsfw/lvl_4/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png b/assets/dolphin/external/nsfw/lvl_4/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png rename to assets/dolphin/external/nsfw/lvl_4/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png b/assets/dolphin/external/nsfw/lvl_4/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png rename to assets/dolphin/external/nsfw/lvl_4/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png b/assets/dolphin/external/nsfw/lvl_4/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png rename to assets/dolphin/external/nsfw/lvl_4/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png b/assets/dolphin/external/nsfw/lvl_4/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png rename to assets/dolphin/external/nsfw/lvl_4/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png b/assets/dolphin/external/nsfw/lvl_4/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png rename to assets/dolphin/external/nsfw/lvl_4/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png b/assets/dolphin/external/nsfw/lvl_4/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png rename to assets/dolphin/external/nsfw/lvl_4/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png b/assets/dolphin/external/nsfw/lvl_4/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png rename to assets/dolphin/external/nsfw/lvl_4/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt b/assets/dolphin/external/nsfw/lvl_4/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt rename to assets/dolphin/external/nsfw/lvl_4/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png b/assets/dolphin/external/nsfw/lvl_5/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png rename to assets/dolphin/external/nsfw/lvl_5/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png b/assets/dolphin/external/nsfw/lvl_5/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png rename to assets/dolphin/external/nsfw/lvl_5/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png b/assets/dolphin/external/nsfw/lvl_5/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png rename to assets/dolphin/external/nsfw/lvl_5/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png b/assets/dolphin/external/nsfw/lvl_5/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png rename to assets/dolphin/external/nsfw/lvl_5/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png b/assets/dolphin/external/nsfw/lvl_5/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png rename to assets/dolphin/external/nsfw/lvl_5/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png b/assets/dolphin/external/nsfw/lvl_5/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png rename to assets/dolphin/external/nsfw/lvl_5/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png b/assets/dolphin/external/nsfw/lvl_5/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png rename to assets/dolphin/external/nsfw/lvl_5/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png b/assets/dolphin/external/nsfw/lvl_5/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png rename to assets/dolphin/external/nsfw/lvl_5/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png b/assets/dolphin/external/nsfw/lvl_5/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png rename to assets/dolphin/external/nsfw/lvl_5/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png b/assets/dolphin/external/nsfw/lvl_5/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png rename to assets/dolphin/external/nsfw/lvl_5/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png b/assets/dolphin/external/nsfw/lvl_5/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png rename to assets/dolphin/external/nsfw/lvl_5/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png b/assets/dolphin/external/nsfw/lvl_5/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png rename to assets/dolphin/external/nsfw/lvl_5/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png b/assets/dolphin/external/nsfw/lvl_5/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png rename to assets/dolphin/external/nsfw/lvl_5/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png b/assets/dolphin/external/nsfw/lvl_5/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png rename to assets/dolphin/external/nsfw/lvl_5/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png b/assets/dolphin/external/nsfw/lvl_5/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png rename to assets/dolphin/external/nsfw/lvl_5/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png b/assets/dolphin/external/nsfw/lvl_5/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png rename to assets/dolphin/external/nsfw/lvl_5/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png b/assets/dolphin/external/nsfw/lvl_5/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png rename to assets/dolphin/external/nsfw/lvl_5/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png b/assets/dolphin/external/nsfw/lvl_5/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png rename to assets/dolphin/external/nsfw/lvl_5/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png b/assets/dolphin/external/nsfw/lvl_5/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png rename to assets/dolphin/external/nsfw/lvl_5/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png b/assets/dolphin/external/nsfw/lvl_5/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png rename to assets/dolphin/external/nsfw/lvl_5/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png b/assets/dolphin/external/nsfw/lvl_5/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png rename to assets/dolphin/external/nsfw/lvl_5/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png b/assets/dolphin/external/nsfw/lvl_5/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png rename to assets/dolphin/external/nsfw/lvl_5/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png b/assets/dolphin/external/nsfw/lvl_5/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png rename to assets/dolphin/external/nsfw/lvl_5/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png b/assets/dolphin/external/nsfw/lvl_5/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png rename to assets/dolphin/external/nsfw/lvl_5/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png b/assets/dolphin/external/nsfw/lvl_5/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png rename to assets/dolphin/external/nsfw/lvl_5/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png b/assets/dolphin/external/nsfw/lvl_5/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png rename to assets/dolphin/external/nsfw/lvl_5/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png b/assets/dolphin/external/nsfw/lvl_5/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png rename to assets/dolphin/external/nsfw/lvl_5/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png b/assets/dolphin/external/nsfw/lvl_5/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png rename to assets/dolphin/external/nsfw/lvl_5/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt b/assets/dolphin/external/nsfw/lvl_5/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt rename to assets/dolphin/external/nsfw/lvl_5/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png b/assets/dolphin/external/nsfw/lvl_6/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png rename to assets/dolphin/external/nsfw/lvl_6/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png b/assets/dolphin/external/nsfw/lvl_6/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png rename to assets/dolphin/external/nsfw/lvl_6/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png b/assets/dolphin/external/nsfw/lvl_6/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png rename to assets/dolphin/external/nsfw/lvl_6/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png b/assets/dolphin/external/nsfw/lvl_6/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png rename to assets/dolphin/external/nsfw/lvl_6/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png b/assets/dolphin/external/nsfw/lvl_6/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png rename to assets/dolphin/external/nsfw/lvl_6/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png b/assets/dolphin/external/nsfw/lvl_6/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png rename to assets/dolphin/external/nsfw/lvl_6/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png b/assets/dolphin/external/nsfw/lvl_6/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png rename to assets/dolphin/external/nsfw/lvl_6/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt b/assets/dolphin/external/nsfw/lvl_6/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt rename to assets/dolphin/external/nsfw/lvl_6/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png b/assets/dolphin/external/nsfw/lvl_7/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png rename to assets/dolphin/external/nsfw/lvl_7/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png b/assets/dolphin/external/nsfw/lvl_7/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png rename to assets/dolphin/external/nsfw/lvl_7/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png b/assets/dolphin/external/nsfw/lvl_7/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png rename to assets/dolphin/external/nsfw/lvl_7/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png b/assets/dolphin/external/nsfw/lvl_7/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png rename to assets/dolphin/external/nsfw/lvl_7/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png b/assets/dolphin/external/nsfw/lvl_7/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png rename to assets/dolphin/external/nsfw/lvl_7/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png b/assets/dolphin/external/nsfw/lvl_7/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png rename to assets/dolphin/external/nsfw/lvl_7/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png b/assets/dolphin/external/nsfw/lvl_7/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png rename to assets/dolphin/external/nsfw/lvl_7/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png b/assets/dolphin/external/nsfw/lvl_7/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png rename to assets/dolphin/external/nsfw/lvl_7/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png b/assets/dolphin/external/nsfw/lvl_7/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png rename to assets/dolphin/external/nsfw/lvl_7/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png b/assets/dolphin/external/nsfw/lvl_7/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png rename to assets/dolphin/external/nsfw/lvl_7/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png b/assets/dolphin/external/nsfw/lvl_7/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png rename to assets/dolphin/external/nsfw/lvl_7/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png b/assets/dolphin/external/nsfw/lvl_7/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png rename to assets/dolphin/external/nsfw/lvl_7/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png b/assets/dolphin/external/nsfw/lvl_7/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png rename to assets/dolphin/external/nsfw/lvl_7/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png b/assets/dolphin/external/nsfw/lvl_7/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png rename to assets/dolphin/external/nsfw/lvl_7/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt b/assets/dolphin/external/nsfw/lvl_7/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt rename to assets/dolphin/external/nsfw/lvl_7/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png b/assets/dolphin/external/nsfw/lvl_8/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png rename to assets/dolphin/external/nsfw/lvl_8/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png b/assets/dolphin/external/nsfw/lvl_8/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png rename to assets/dolphin/external/nsfw/lvl_8/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png b/assets/dolphin/external/nsfw/lvl_8/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png rename to assets/dolphin/external/nsfw/lvl_8/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png b/assets/dolphin/external/nsfw/lvl_8/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png rename to assets/dolphin/external/nsfw/lvl_8/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png b/assets/dolphin/external/nsfw/lvl_8/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png rename to assets/dolphin/external/nsfw/lvl_8/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png b/assets/dolphin/external/nsfw/lvl_8/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png rename to assets/dolphin/external/nsfw/lvl_8/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt b/assets/dolphin/external/nsfw/lvl_8/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt rename to assets/dolphin/external/nsfw/lvl_8/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png b/assets/dolphin/external/nsfw/lvl_9/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png rename to assets/dolphin/external/nsfw/lvl_9/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png b/assets/dolphin/external/nsfw/lvl_9/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png rename to assets/dolphin/external/nsfw/lvl_9/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png b/assets/dolphin/external/nsfw/lvl_9/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png rename to assets/dolphin/external/nsfw/lvl_9/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png b/assets/dolphin/external/nsfw/lvl_9/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png rename to assets/dolphin/external/nsfw/lvl_9/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png b/assets/dolphin/external/nsfw/lvl_9/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png rename to assets/dolphin/external/nsfw/lvl_9/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png b/assets/dolphin/external/nsfw/lvl_9/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png rename to assets/dolphin/external/nsfw/lvl_9/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png b/assets/dolphin/external/nsfw/lvl_9/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png rename to assets/dolphin/external/nsfw/lvl_9/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png b/assets/dolphin/external/nsfw/lvl_9/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png rename to assets/dolphin/external/nsfw/lvl_9/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt b/assets/dolphin/external/nsfw/lvl_9/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt rename to assets/dolphin/external/nsfw/lvl_9/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/manifest.txt b/assets/dolphin/external/nsfw/manifest.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/manifest.txt rename to assets/dolphin/external/nsfw/manifest.txt diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Cry_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_13.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_13.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_14.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_14.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_15.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_15.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_16.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_16.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_17.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_17.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_18.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_18.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_0.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_0.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_1.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_1.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_2.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_2.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_3.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_3.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_4.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_4.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_5.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_5.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_6.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_6.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_7.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_7.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/meta.txt b/assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/meta.txt rename to assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_13.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_13.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_14.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_14.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_15.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_15.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_16.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_16.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_17.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_17.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_18.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_18.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_19.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_19.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_20.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_20.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_21.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_21.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_22.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_22.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_23.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_23.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_24.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_24.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_25.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_25.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_26.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_26.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_27.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_27.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_28.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_28.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_29.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_29.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_30.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_30.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_31.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_31.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_32.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_32.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_33.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_33.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_34.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_34.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_35.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_35.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_36.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_36.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_37.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_37.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_38.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_38.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_39.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_39.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_40.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_40.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Mods_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Painting_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_0.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_0.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_1.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_1.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_10.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_10.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_11.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_11.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_2.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_2.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_3.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_3.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_4.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_4.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_5.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_5.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_6.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_6.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_7.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_7.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_8.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_8.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_9.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_9.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png diff --git a/assets/dolphin/external/L1_Recording_128x51/meta.txt b/assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/meta.txt rename to assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt diff --git a/assets/dolphin/external/L1_Sleep_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Sleep_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Sleep_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Sleep_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Sleep_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Sleep_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Sleep_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Sleep_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Sleep_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Sleep_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Waves_128x50/frame_0.png b/assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Waves_128x50/frame_0.png rename to assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png diff --git a/assets/dolphin/external/L1_Waves_128x50/frame_1.png b/assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Waves_128x50/frame_1.png rename to assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png diff --git a/assets/dolphin/external/L1_Waves_128x50/frame_2.png b/assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Waves_128x50/frame_2.png rename to assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png diff --git a/assets/dolphin/external/L1_Waves_128x50/frame_3.png b/assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Waves_128x50/frame_3.png rename to assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png diff --git a/assets/dolphin/external/L1_Waves_128x50/meta.txt b/assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Waves_128x50/meta.txt rename to assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_0.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_0.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_1.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_1.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_10.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_10.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_11.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_11.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_12.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_12.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_13.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_13.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_14.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_14.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_15.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_15.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_16.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_16.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_17.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_17.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_18.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_18.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_2.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_2.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_3.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_3.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_4.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_4.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_5.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_5.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_6.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_6.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_7.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_7.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_8.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_8.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_9.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_9.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/meta.txt b/assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/meta.txt rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_0.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_0.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_1.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_1.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_10.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_10.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_2.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_2.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_3.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_3.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_4.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_4.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_5.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_5.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_6.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_6.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_7.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_7.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_8.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_8.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_9.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_9.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/meta.txt b/assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/meta.txt rename to assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_0.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_1.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_10.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_11.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_12.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_13.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_14.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_15.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_16.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_17.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_18.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_19.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_2.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_20.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_3.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_4.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_5.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_6.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_7.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_8.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_9.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/meta.txt b/assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/meta.txt rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_0.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_0.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_1.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_1.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_10.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_10.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_11.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_11.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_12.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_12.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_13.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_13.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_14.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_14.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_15.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_15.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_16.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_16.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_17.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_17.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_18.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_18.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_2.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_2.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_3.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_3.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_4.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_4.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_5.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_5.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_6.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_6.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_7.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_7.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_8.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_8.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_9.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_9.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/meta.txt b/assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/meta.txt rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_0.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_0.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_1.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_1.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_10.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_10.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_11.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_11.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_12.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_12.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_13.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_13.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_2.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_2.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_3.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_3.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_4.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_4.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_5.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_5.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_6.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_6.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_7.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_7.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_8.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_8.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_9.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_9.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/meta.txt b/assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/meta.txt rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/sfw/manifest.txt similarity index 100% rename from assets/dolphin/external/manifest.txt rename to assets/dolphin/external/sfw/manifest.txt diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png b/assets/icons/Animations/Levelup1_128x64/frame_00.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png rename to assets/icons/Animations/Levelup1_128x64/frame_00.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.png b/assets/icons/Animations/Levelup1_128x64/frame_01.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.png rename to assets/icons/Animations/Levelup1_128x64/frame_01.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png b/assets/icons/Animations/Levelup1_128x64/frame_02.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png rename to assets/icons/Animations/Levelup1_128x64/frame_02.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.png b/assets/icons/Animations/Levelup1_128x64/frame_03.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.png rename to assets/icons/Animations/Levelup1_128x64/frame_03.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.png b/assets/icons/Animations/Levelup1_128x64/frame_04.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.png rename to assets/icons/Animations/Levelup1_128x64/frame_04.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.png b/assets/icons/Animations/Levelup1_128x64/frame_05.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.png rename to assets/icons/Animations/Levelup1_128x64/frame_05.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.png b/assets/icons/Animations/Levelup1_128x64/frame_06.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.png rename to assets/icons/Animations/Levelup1_128x64/frame_06.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png b/assets/icons/Animations/Levelup1_128x64/frame_07.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png rename to assets/icons/Animations/Levelup1_128x64/frame_07.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png b/assets/icons/Animations/Levelup1_128x64/frame_08.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png rename to assets/icons/Animations/Levelup1_128x64/frame_08.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.png b/assets/icons/Animations/Levelup1_128x64/frame_09.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.png rename to assets/icons/Animations/Levelup1_128x64/frame_09.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.png b/assets/icons/Animations/Levelup1_128x64/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.png rename to assets/icons/Animations/Levelup1_128x64/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.png b/assets/icons/Animations/Levelup1_128x64/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.png rename to assets/icons/Animations/Levelup1_128x64/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.png b/assets/icons/Animations/Levelup1_128x64/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.png rename to assets/icons/Animations/Levelup1_128x64/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.png b/assets/icons/Animations/Levelup1_128x64/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.png rename to assets/icons/Animations/Levelup1_128x64/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png b/assets/icons/Animations/Levelup1_128x64/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png rename to assets/icons/Animations/Levelup1_128x64/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png b/assets/icons/Animations/Levelup1_128x64/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png rename to assets/icons/Animations/Levelup1_128x64/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.png b/assets/icons/Animations/Levelup1_128x64/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.png rename to assets/icons/Animations/Levelup1_128x64/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.png b/assets/icons/Animations/Levelup1_128x64/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.png rename to assets/icons/Animations/Levelup1_128x64/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.png b/assets/icons/Animations/Levelup1_128x64/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.png rename to assets/icons/Animations/Levelup1_128x64/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png b/assets/icons/Animations/Levelup1_128x64/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png rename to assets/icons/Animations/Levelup1_128x64/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.png b/assets/icons/Animations/Levelup1_128x64/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.png rename to assets/icons/Animations/Levelup1_128x64/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.png b/assets/icons/Animations/Levelup1_128x64/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.png rename to assets/icons/Animations/Levelup1_128x64/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png b/assets/icons/Animations/Levelup1_128x64/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png rename to assets/icons/Animations/Levelup1_128x64/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.png b/assets/icons/Animations/Levelup1_128x64/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.png rename to assets/icons/Animations/Levelup1_128x64/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.png b/assets/icons/Animations/Levelup1_128x64/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.png rename to assets/icons/Animations/Levelup1_128x64/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.png b/assets/icons/Animations/Levelup1_128x64/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.png rename to assets/icons/Animations/Levelup1_128x64/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.png b/assets/icons/Animations/Levelup1_128x64/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.png rename to assets/icons/Animations/Levelup1_128x64/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png b/assets/icons/Animations/Levelup1_128x64/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png rename to assets/icons/Animations/Levelup1_128x64/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png b/assets/icons/Animations/Levelup1_128x64/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png rename to assets/icons/Animations/Levelup1_128x64/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.png b/assets/icons/Animations/Levelup1_128x64/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.png rename to assets/icons/Animations/Levelup1_128x64/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.png b/assets/icons/Animations/Levelup1_128x64/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.png rename to assets/icons/Animations/Levelup1_128x64/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.png b/assets/icons/Animations/Levelup1_128x64/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.png rename to assets/icons/Animations/Levelup1_128x64/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate b/assets/icons/Animations/Levelup1_128x64/frame_rate similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate rename to assets/icons/Animations/Levelup1_128x64/frame_rate diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png new file mode 100644 index 0000000000000000000000000000000000000000..bf97f8d6ea7ee9d0df8714b06ceb37d5231d9444 GIT binary patch literal 1326 zcmaJ>eQ4Zd7|&g6?MkbT;lNE?$Og8M!GJD&u*4t@s!)f0n@AX1^NtUGE zwXE(eMNn}?w!u(_-xWbhL2;;HYyB7sbt+TrT-gM5)wQVqtVr4Fd%a%WA7vrQ`@CSAqj0<8t~M5`;%`?uBAdLU1l?)I_;8M>Nd*q&jT zc+nzItv)YIAhM+>vUWVaX4rIBbA_@-=YdVL6hmjT#n4u?T`vLur?IZKo9wqCoq(>@ z=V+)T>Fs4OO5e>L6%`R1=^@8JbpgGyrS)Z@>BZzQfxb|>p-VvkRRDlWY5|rdIVfl( zP}4lA1{nhYvZ6Hdfl!OWwKOwjY|L~$U}~`J z+d#fov5hyyO05nAp5-JhtGC<;9U05B+>B-898WQLeWzjSR?clG)~FP+3?1W1sPA&D zEWVOt$ykQ~;2RrxPK2b2I-L|$9Z9+xBt?M-X+h8gRz(4n@cIqOEA=TlK|$DME)v7S zbUFuXLm!ooP;(Z=wZTA}wb6nmnkNSh z&fzcZ4QS}9I`$?Nv@S0%zcus9EQa~0nB1JqpPxVZ%WYG5?eNcwM-1)m-TP;{=EwHW zU*o8MK6xZ_yPS9@x%0&Cx;@Xoeqr{kur%s_Z20uc2Oi#d_3Bpt^b>x}MeXBcF|a?fYY3 zU*Ux}E@Q7IW`dK~Dvw=1z3Fg!bc-_Hf7Um5{tMXm@6vSS7`7+(_V4dD%<|2PKXh(9 t_vj_AuwmrN!R4ChpjYcZd2uuCtHQXETaTW4F5FZ6gT<6~d90;t=s&rU%P0T< literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..39c910d3a41314c985e4ce79337cbbf30d1ae79e GIT binary patch literal 1597 zcmaJ>eM}Q)7{5}5%D^dpRi@)O7`W*5?s^B;R;rYKC`z%G8Y+Q}>)l)EN_*Y)Sc`6o z=qAjeQ#WD)}yh;LPt4dw`DnG9mu!RfIf~Wx+2mqNwqk+0$*bp^hBX$i?jvW&i zI-(-`jo2tDx7~r7MG2s+hQfK0BvDqc0RpG>&|Z$8hiXYJMNsf&af&r)X#=H4$1V&y zlmxHAX|avDgF7QuE6X7RK|~@EO@!8nQVl`r^?HKT5?U<|HSlmlQ0Ag|Fq{>)U;$xX z@`Yrd7(`SmTZhvAp7#_;NdvV zgY$V5g=+!BA+9YtBqdTWuzYR#wf^hH)} zk%^3Bi-TdA3-TcD+XsD*VNK7)8q5;lWKnX7VqLru9JQh>hHJ$Ts--m~I;Y$h6vRmQ z)mV*2L$(0P_ZARrk{Ccol5Frjg8)b8=hIpi;2zlNIO7qZBzSZ<%g~&cVR(k}zyL<^ z!c)l`?NfLHg9ydB7)T0u^LR2J2Y}JzJj+nHhbFbSmt$F)CMg}muowX;60yE{s*mDF zusLJmlfi(SC!+;|aGoSMIBLq(1&1!tQR1?~b$EDq|K+_`5G27^Y%#f_f823bZBb^b zgnx(k{&b_X%C@ij%eKlR1xz4AxW=oeHrzdS@wL@=Po(b|Y|8y{=6&U!_b23JK6qSl z=Ul(3U+Z>w81&36ROoa>Vw};AiR+k0+1z%t8NFY0jwL6AiTw!i~cvHBP6VAaH(t%~zvICA{Agl`I%Vxu z68zSrmC0!dz3ofM=AK;@KX*2Lw5i2p+ufM1m_OK}@82}P>xjFuZtgeW@!@ny9%#*) zwmG$JIs3-vs-E7n-E*90d}+P&5)voBGI-Sa>GrGlauUsV&W&$Ps>}&tmW&aP|S$*a?s)anPWU)!;^ zL#VILC2LNl&CYCZzS55@y|-=+x*uu2(bC40EMboCNF}Cqe)a1irm04ewgSz*9By^% z53in~QmPX@4W{I>O!-`%ZUS=a#>U^Lq$RausA>A~+>cfMi{lcGP4_BNmUXd;&QI)Z z==qd;({~>{d1mb{bt%%QGTu(SaN34cx>QKxEJgeM{Y=w%WCnXCBX!LKlhR`U;qFB literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..4975adf8627b62a22d002bfc1d7be423dcc5e83b GIT binary patch literal 1754 zcmaJ?c~BE)6kiEqM1gwIQV`cr5o$NPn}ZyZawR~JAjTlrk&(@2V^T;qCJO`*jYwN? zzJ0&UL^uF2iuI~ADov3n#z_MkbLz0I1`~}2fba;b3Bxl93RDvt^hOD5;Pe?5sMknX zseA>ZFvSo$eSE%|Se>7w!t*n6k%krVDHv`Q(*_I#g@IN>meC@%N>~$i#kA}^hFRc* z3Y95gO_EAeB!e-enE(ZB6v7b%0R;fhfaICZgT zhh~jdtdz!1xubUymX4xKVi?ZN&1L6u*`#>`jEY1e7~#Mi4n%7}mOLYcSs|k($YnuF zSa7r6MCnN*=(LEb$!to(qRCE1VK6BaZwwnPQ@NtE23s)`jIt5fU~q;up>3g*#D6s2 zscli^nFv@(SjcQMPM1U*c(#EsQgW$;MSo#y^ct}c zBh(m%sUe(?q7aAR;Si?Qav`-4!7-r*!?^;X%g(#-B9TB8%jHA~I5HuM%3?&aXsJvV z#^sBmWt`|J#D$d`Efi+N372m@?RyF<`d_R##!O%oX;zVBma7qxbtFYvbfgL7aM=hL zlBzdq$XrXPvqqC4O9`_+htR~DNdq{MWU>Ao1Tbz`7?&d;AT{0T5Kpb4C5>7L33yyg z%j4lZR80pkiPyZ9yvaVLPv{^_E-qpOCA54T34;iNCxUPR4~5iRgac_Yfq=_JP$7>e zV8Jv+*x5I4^^xm{ZceB8W-#c@o6#bS^gNmA!MWz%;YAOfM{=S{Mz0Qs<3MNiJpgd) zl1rmh)~^1wcgr6m%w~t9I_nxTE`~My=&O-8 z^gpbe?g8|F%k{EVr;;O0dg4dtG7da+A?1Toh{ro$cLX)S$u4NTs#)APzctwFBm`J-;zH4 zG~j+QWNYc%rB@myO~`n2pzYl30R}T3&QPu?0OthkE<)vN@N<2Uvm*Pq+pGAEmix8S=nd9m#njz_o z)mNE70C33D#l~~-VT%Zg)r&~Gj>xJv@@N1ER9KB9wTj_D4U?=h$b~)C7lfdWmJ8$j z!;mmz2$P~)kZEFKGQ(r3%vF?(7Aod~fmVVypl3J|wCYn0X2L2LzP3y7vi%qqg0EG$ zRdV4Vsrax+FoZQRATB~73PBJMmx&mf#57_pNzDc^1Vdqzzi|k~2~13&GH~b;@(xY3 zmWWoWhurZyxiE#}j06l@EEbVPEMiT`Fe;PDU<89P4B|B)bB2Kugd(LhNr`dku$_0|Wini*7Go+LQ%O-&6(UmwD^;ohvA--> zg$2(;hOwarGe;UIX4tom_dSGFz86b`m>7~{O|dMSI@}16DJ;jDQ&=O2iA4zL8>chS zti|kSuhC%0O2(v1XK1yF)q}5-Oz7T0fD{J=h%uajG<>H+5)I8uv_=Zy5;3WjNGJ)a z;R6`N({Ckjuuu6DJ_zG57Xm>Utv`hXKnx?1K@=`QA&nTpAT5dGVljeBB@$c+^Aurw z-@MgF!$*8`+Qm16!EfG-7GvP&$;1!Nn#4Z)*f~WmidFIJz`#IRsv7#R+qh3KqUKV$YLpsF+-C>Ad1 zW5>=dVf}1sI?^UO=z_-BRDvKiqR!gx~J?s>wfxY|O9R z=3<%TTyv!--`al0bHepClGHZaLthvA+^UGKm8UDa^XhJ&LO!34xwW?BwHEl^*cY-~ z<8`=PGTBGH@UQB_-Fu367GaZe+zy?sU%%opS!1ew<>HilXz7!K%hYRTwIl^?#-}*f z1@ul;6k?`QBD!{0eq6!w)V()%oo;Mv&+BX<;EJruyw*DRnNDc~;QgB4(jyyYOl#V4 zKjtX6d&H=T8#b$@NiiP<{E*-MHL}KC;E)jSZWDr^Y&Z%6BZIC-Z|b#-?HQLFTsSJS z@$Q#_w%gOBZtV~6c9!ORQ`9$FQ0rII=XiRz?nsY&dH2H2<^3{|#OCdI(@=P&b)sPW zyMOp7w6(KXWo40nJ zY|eY}PX@?#b!4yazvau+ju3P#d)l$6(`iE4%ZY-9f5|pwy$K*30`J*m9hcRstx#3CvLZO?i~j&Y CQO3gn literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png new file mode 100644 index 0000000000000000000000000000000000000000..e6c88df92b73b89f1ad31810be1a653c3fea5d6f GIT binary patch literal 1686 zcmaJ=c~BE)6we_o2w0(7EJ|I2w$PgFkwB7#KnMwk5ag0$7>>iMhk9}@h1G@VD8w4dC-Ij zQz+w2lFCpgLXotYg2V!pPa+5ciKPNcOJEwIjvyC97=ocN3SKcE72}u?N2Soz!vg`$ zS{ z2o0YMLQy_OiAX-7(Fyq)2|^MQEkO#!5~rUR;iXcsG)gGM#F$EgqN+%#N}*J#f`vg+ zg$h$dAWm$o(ZUc$l5)n@gVBBu}%3uuP66woFJ6ABP0AYE_N z(pF2L=2MhN$|uDll&=vY7+*(-#X=#1N<<gi@&~&dP#;2;lXt&#&t~K0bu{c{|l@Y17 z>-RHn7{7%);{LVo+jqBhkqhwFC4EtW3*T<$zCId!FiEzI1fFWbckNm0!wC`F3?{`-k_IuBzxtei%cnRJ}L)M0R{}2G+6F zZG*9Y7OSOh-!=B>@A8IMxp8A|xO=x=!2Y(+gJ-d;8f8QB1>^h2=QL`k^?dIQyM-j| zc4sTxD_T;{yi;1W<)2XO1@7zLRZQU{WAjPZ3s_`D;x8k!Rx(r zKja`3H`3xc5OAPI-m|UaXIjwS9Q;g z|NJ!wnLSd#RzRm}F3x!OXko;eggHLEjEeb+)zxy`<>_YMQx@?^U1w9t;}c1K@m{7o zOMYbiaeBb(0uyc-z1&v+X;R|B+QqeBmLg>`K?T>>PuurH!->W=`<=?xPu9n?my3`3 z&mOS$&p(%)uDO_#>eK7qD+r0I9N;9mtXS)}Fnc&`tj4{o`pL{(#jZ!9gTHL`??_vJ zfKzP`85l_E${B5GU~kg*br<+>+p_$N<2uSbpn2@F!~NZyb)lDw!Z{wSWiB%sx(wTa PLRqX>Rf6(}B5%!qVpw+j literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png new file mode 100644 index 0000000000000000000000000000000000000000..e7bae4d6c8e7b60de8cb5d4751ae8d9259a39338 GIT binary patch literal 1672 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 literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png new file mode 100644 index 0000000000000000000000000000000000000000..32e864e9827420142930083d480d331024d033f9 GIT binary patch literal 1540 zcmaJ=eM}Q)7(ZwQL|`a!rcUQsm~or;U3zV$($W^GvxQU^s++UdySJr6@4EJ&1vM*% zeI;gS%*lpcNfsA0gc z;4`?ar4Pfwol#va%OL|nM59qW%HUGiPf&Wjo*-$0rZK32MH)nzi(z6UKk2~=B78Uy zk^_>6COmRpsZKVkA=u$8f+2_Fkz+CPuvV~YM2rg&6iyPsU?R04?TG9G|HF8qcBHZ) z1PB+1NOfTzw#1j8G=}ASdZ2_NL}Mrq2Vh^gTC2p@1%W8rtwuGx!G(Ze&~d=aahw<9 z3n>btfriI8uaCjJI+Evf0>?9~F6rk<_9djw!f16imbU3A%4X5qN~|`UmMPSi*ys{7 znY6Ww5t$Qtkc=IGvCV7=tEKhkr`j4UVZg~!xKfg8lSHVfmSibXErn2;!AW##RX`M^ zXynC2kA_pWf^eW72&G{uhz=Fm5O@Lrj?rovngy5_G9A-+1tgoGE;=1Mw!5dLIL>q&-FN=xw&*uC4Znihe$~NUAEd-L-0kus zvktd4b)`E?wVS_whgh+2EWSyhisJ_sDkr2?m?x%AIPEvRi)_AFgWT)A-;$?9Zk`oV zLVYi%ps58@;_dEc&+@jWF$=~m*faKDX(zHZXH?WP`ciFt`c{|Fvuq@?>tuWXt{Vb! z&3Wz*d$02K6q9mm8tck5QLDT1*md>GvZ{VYZ1vUYuDk-xr(((|(;@q++&ZNF1LPs~qy>5Nmh?FjI;C+jq@u}~$RyFRM)b3xp;ltj4 zUf7$r<(&U% zE4lk;thumebThty%=r6+vRkAVy);)DQv5hN>$`!&x?{7-dKcfxHhtdGbotBIkc-zp zt~M^wT)%m*{C>Ksr98cC%PMNJYx@)?@A-Qd8ZCi=E6$PI?~k0V=*Uv$A3kUNA@v~h zY7sUVMW+zEytcAL|>t;DnRKT5(UD*ylh literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png new file mode 100644 index 0000000000000000000000000000000000000000..c692f48952c106373b56ecafd4bce7ba1295f5da GIT binary patch literal 1557 zcmaJ>d2ka|7=JXS#89*jgNP!m+d9ax$sTDpS(;usXn;T{(Nd zzTbE48t1HOBQ$Sl001NG#g-DZE=Ny+T7}*n>U9pZj8Uv*%3Rr}MENiTgNH=xd5nEiLNrG%rTP_l7>HN@+n_tNGvR(k32{1IG#b@L8Lb?y)KNyGQAg4`nkJA25vh?BK1N89%%lYij0j;d zq=>SFB`oqDxmq#di0nWV!H~o8)UXs8%oWO-F2;v+l$O*5gNe}kwIfOi{GY~iwIi;Y z5Y&~xh+G{OP)U55Nia&^iyb8t5gTrHSVVQ<7g}VYItV4jZZYBLi`FZ8IRg(pJkNUw zA)BHI8tMgt_xKpXV;}|I;N=B|H6-mk4_`zY%uJ4vW@)Q|qO4}4wa8+%<}lgDA}d`~ zNG7p%DWdR_0F%B&&{G~1zyRL+ zO!5Z$6rG?TLP;(hNkLz>K;{q->WzfJ>M6p*kTl`rS(af)%AnV?xDHX&CHm%>K1v>; z=1hoB2Lo-Mjuw>AJcZHVd_T8g6B@cS=S-Ist?_t#_wU>O03c^_Er#$s`}2At6RG*kH%iwIIOcLFlAe36?42` zd`H8NV|%w=Ik>{cJos)yU)9OG6WtnTd|p~cdKc)+S^(;2&%4_4jr#TWo~>!v`$I_3 zvoG$iyP*Gc$nt#$mMrTc?^4j2(rjCMyj&wy2`SEwopXM!ZT;kwq%d6%v)^Xy1k%`}eLa8Mr}TBFU}|SM{nDF;MJcVm>CKkZ6<_sg3WvFCvsz!Sm}nk; z;m#rV%4Nqss3XP{gY-=s0@>?ZeiF@xx#8B{Yx# literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png new file mode 100644 index 0000000000000000000000000000000000000000..fb1c8bb9042d28b4e0ace883ea20e23cccf7ea52 GIT binary patch literal 1551 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 aR#*bwkfy%NiRRUj|9VblzU`o;xcWa;e@A@) literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup_128x64/frame_rate b/assets/icons/Animations/Levelup1_128x64_sfw/frame_rate similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_rate rename to assets/icons/Animations/Levelup1_128x64_sfw/frame_rate diff --git a/assets/icons/Animations/Levelup_128x64/frame_00.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_00.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_01.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_01.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_02.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_02.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_03.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_03.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_04.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_04.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_05.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_05.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_06.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_06.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_07.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_07.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_08.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_08.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_09.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_09.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_10.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_10.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate b/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate @@ -0,0 +1 @@ +2 diff --git a/assets/icons/BLE/BLE_Pairing_128x64.png b/assets/icons/BLE/BLE_Pairing_128x64.png index 34068c300386e0c88e45b40a8995b0a4360ed79f..f60598005d41ff05a9f763f42f1a6b7900150e33 100644 GIT binary patch delta 2594 zcmV+-3f=XC60#JKD}N5d000id0mpBsWB>pObI=R4=zX=Q&()_~UnnZv^fJA^~ z5&Z2M1W-tKxb!X4;(m5s;;gMOn*oOh-TZiZF6&T;{}sU=HlYgu3ftqFJ8F1x=I9yW0{$md3kw!W+F-u z1pfa14Gj%%-n@|r5XTA%3Ydd{*kCY3MMVV$26m*Fy}dp1aF;G!zI^#25nvGmvXPOI zojZ5ly?Zw_CnO}qXf%HL$1YsBkdu@1_3KwD2UrSv2Y&|#D=VwluV1I9r)xACcXxM) zK6Tpj=g(_uY9s>0HxhbOrPNvz%0r!oIih!Dt(t&w@;)jVi%}|LV2-yCs zP$;^0?|P;~gN==iBtfPP6{l9KjfshYCa}#ZD=Ry8>=<5&o&d<&+1Wv4 zMvfe*)9I?KtAGCd2@VPi3&CY_a&l~JtjT>_yMF|cwtf3{NOpXDJO@%vPEIps%)oVd zc{#cPXpu^#Dk>_vb?cT|t%f=Y+OdEC{@_oNp#NLHe*M_~6%Y^re=jaBCUf<-Z{MKo zNl8f%9R6Flxw(xQGX^5op+kpPuU>ur{8^HqHvazpNC3DX$|2{!jW#DYHy7De=*r>Y z;eTKbp5DHFdjt?72H}#QpI=c?L2aj7I{|0~o;-Ond-m+HW5Vs^1q&9eT)EQM*B9}Gq>`4F22aP^AvN{&^+*!TB;VQz&}cN*u3ZB(xJD9& z`@`8G5ogbyCF=AWH*VC{)_Qw;n_LQheSh!Xz2nD^XJllcss8cfht!DiHB{?(-LYc_ zUQtTob;*(?n>TOXuwg@RaPX#0o8ZnS7lG6~c<|uYuV1tiB#8jihNOzJ9c~K{34uO% z@L*I_)UI8-5)u-kqoesO0+omIpb0?4SdajYuKHhxI~3VyG{(in0d&;(PoF*o6MwyW z^)hcY1kjKnLktFkMb3gqlsUTUe;p;bLzMr^mMu$3NtrckmO`Ppd-txNpC6boZ$ToL z3?DvRWCEzTb32qtN`6mnluG5IMT>6Uyb0*HY}rC~XAuz*g4W5*%mja^d^|in#36w6 zdb4AkBV0srVNuo@FkryDckhq}sDFbIZ|4<2lC3+gfI`Sa%y0goR)9x-A>lL(*+ zj)nABtT<@;Ix?9oJ3AZQ0M(aw?%au2=2HP;$Du=ql1JIZ87k4x!LSs%Eu?f;S64J* zf(fuR^fV3x!N)eai5$H7+a^qyK#rgooiu6EgsBKr%%__)$wcsQ2b1vKAjwaq@bsrHf`FJDO1A2!q6Bq zwI184R4QD=$H${vZWK3^w%`WnpF4LhIXU^{$&-yd5o>1XIZ$pQ2SBY;r%sR&2)B=q z50as+tt}rW3JMD3a`~V^gUFGLjEvEvN24kwS0=sp8HGBT3wapR>+m%O~Z zs3XXtP!H+|^o07^b8~Z{>`igmn3$M7d-nYA3rIXcBsQaNX_5v4O%g2T|?Qk$Nh-mP0V41e0#*aQRwfC&^NR#sLH4i2dCp*GZjfPlot#zJbyk*cbyXV0D? z19Q~VAiJ!rOcY6wnZ%unJ_!x^`T20bJ%>3#C?jIZ+OkFA` zCkNH1$sJ2dN)TbV$yKXXiBA9xaauH(0S%u_Lkk5#8<8`SgfCyd442=xZy$Ok;zuu_ z0pnArP9^3b!GA(|ii(Qn&Yes9)&deTD4{k49EQvc3c)CiLP0ky;~C>j^!cAF@T~Ni zOL4!Eria9z5FfZO{5?848cKp3kdl&u@x_Z5U%h&j6mBStlamu|!Ryzrqke}d+1uOm z0TGoYDlTM~6DLm0pFdv^0dNkerRfN06n&;U^WlJJ&3`}xovl-2N)}#MtXKi@xqtsY z;lfCysP?k5vf$SnH*T!2uSbezD~Hg>u3o)5BqRj!AqYxj?3$XI+qZ9HasKaeadCP0 z@FD9_1QT~E`es-0R2rD;FhS?2d6Z1bSa`FEiDZtCK4|SB!~~@nKv}@P9#{r^?A>GQ@;Rr2-j;4jn>RCIty?Fo45j%# zH3H23Y&?%V8`XFI=yQ)Zzk!2_iVAe&JUNhA$oKj8y`mhZw)xErrP1EKdwCIHQQzmz z)ql)vEc8&erOwZJ-W&CVm6d>I^L=jMerG7nHWawD=RBq(z;fvS_weOosISli@``|F z@csX!n{ykHkaz>%07*qoM6N<$ Ef}^?D!2kdN literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG diff --git a/assets/icons/BLE/BLE_Pairing_128x64_sfw.png b/assets/icons/BLE/BLE_Pairing_128x64_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..34068c300386e0c88e45b40a8995b0a4360ed79f GIT binary patch literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG literal 0 HcmV?d00001 diff --git a/assets/icons/Dolphin/DolphinCommon_56x48.png b/assets/icons/Dolphin/DolphinCommon_56x48.png index 089aaed83507431993a76ca25d32fdd9664c1c84..e80fea5bd7f694549da1b45e9f3db056342a95cc 100644 GIT binary patch delta 3373 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 D!U>8P literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png b/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..089aaed83507431993a76ca25d32fdd9664c1c84 GIT binary patch literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj literal 0 HcmV?d00001 diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63.png index 46f559f65f11194c94c15bb7f195a1d72ef2a295..93a7ad79cd95cdfe7789366edd8fadb3355e467c 100644 GIT binary patch literal 5390 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 literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..46f559f65f11194c94c15bb7f195a1d72ef2a295 GIT binary patch literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX literal 0 HcmV?d00001 diff --git a/assets/icons/Interface/SmallArrowDown_4x7.png b/assets/icons/Interface/SmallArrowDown_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..5c5252b167d2f9f9a1ce5e7b9f9c99123879c1b4 GIT binary patch literal 8340 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 literal 0 HcmV?d00001 diff --git a/assets/icons/NFC/ArrowC_1_36x36.png b/assets/icons/NFC/ArrowC_1_36x36.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0c6dd0cb2b7cb6eebb1507fa68a8dbda11b146 GIT binary patch literal 3692 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 literal 0 HcmV?d00001 diff --git a/assets/icons/NFC/Detailed_chip_17x13.png b/assets/icons/NFC/Detailed_chip_17x13.png new file mode 100644 index 0000000000000000000000000000000000000000..9aaa1c5552a2d115b7042a4bee7a0c636ed00f01 GIT binary patch literal 981 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 literal 0 HcmV?d00001 diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png index 1783531285bed514517fc0821501e718359dd765..e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f 100644 GIT binary patch literal 4224 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 E4{AO|uK)l5 diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61_sfw.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..1783531285bed514517fc0821501e718359dd765 GIT binary patch literal 1541 zcmaJ>eM}Q)7{3ZaLqs>76Ex1V5{uLJ`hl&zQLyxiR4J5Flue0VuU9zXKDZvVFu$in zjZPV|k*WB>lBo=wab{s;0v4vrHgq6yL6|@s=rj(*ZALe`w+OgD#xD2qyib0=-}CW4 z_wKW%tO^gC8wNp8xH$>4fiD6cy*LQG!v-3sLy!oej7F>3XoNYQby0aF1bI7aN-wPT zSzjw@m}hD^wN~8M!%5Suc^yp$% z;LAJvZZ<7U;$}9n&swkday_9{j$6k|(n<`UWz^qKx;6aE!&&NN(Msv}w)zef+FQ1w zPdX)Tbt_@>_j_ z1g4yN$-3nhg@rScIaE?HPo@{A*oop?Lg$pk$HB2)6bR6yfWuxok8z`3y<}7u1$MxV zNZ?V3kgJ!xNGj7}g^esv!dkgfMko{tSgVthPF&?syKrI|tWv0yh!WgdnNo|Y)TmMi zk6jWFkaxOJ8D8>lEl2;>9^cDOUul8V6b|{|}|<3A9_V zkuUT8Phq#ch$9gj>1GRf0_>e@Q6LnD8hH{ISl-UEdE_v6wo9ijB}kYxrRx(fq|eo5E&zRs*rRh@+=LHR*h1V=c1idZ;b1lJeL)dauJXW z64={+?e(||3{b$F7+$cL7=MxhGtYzJp6B{075o?>)?~ZM@Am^U<4XHBa7ryUV+Omo z^NSB}*9I*Vo3i}=cBw9McHOY`cJjO|@#%;8vhG*t42yEl!M7sbnHFlhs_u%cu{BLw zhaGH~)>1i`aAdfxzb-yDYp8axDf-aUrDb=dji35@J+L(5s-gM3W`<^_YQ(SZsXOZ| z+MW^|z2V5B)@@rgjw-Cq(dw%_ar$zq^bB!0SN!Wu`o1~8yPWDctj)SpoN(-94Cmev zzx{^qT>j%Hhc_onj|mkQqEx+SO=({jC(e}(-Mu#TV*2AZM;fP!-l&6{t3IlprLTNH zu7%|!`|gA}!?~;;lRx5Kde1<}Db&?0+p;8mU68o-2TkKA6-h@we_Yd@SR^HGPk zTw<~x)L!ghDi?=TUtd5z8164@t6%c>#|vtn#`%LIpusI7pJ(MpaIQm;*_49SCT!aE E4{AO|uK)l5 literal 0 HcmV?d00001 diff --git a/assets/icons/NFC/Tap_reader_36x38.png b/assets/icons/NFC/Tap_reader_36x38.png new file mode 100644 index 0000000000000000000000000000000000000000..4e0ba8f05921ccbfbaa4390ad07e98fd454df00a GIT binary patch literal 3748 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 delta 706 zcmV;z0zLiI2JQurIDZ15Nkl;5(?#D zv~htv4nV-d9K@^)0%me<5#Ize&B1}Y*n?0E8-aj*5C9jnJbwWQ!k@UwfQ>*v=8?-P zj+ngT2l$+2)>F$Jg~A0bNdN!^Kmh<000jV002BZ~0Z;${1$Z;YSrC92fEWNk0Z;${ z1wa7+6aWPPPyiGFY+3Yxqo6!F~AV8 ziy!4bb0@qb0)L%rnJVx9FxfTB%C85YsXYQ1GA_C0Ux~>NW z;gaYrjejUTz>+kl(V56PIstBbs(?Vete;J!O8L2_L}#3UxB|w#F_ik}$qCS#ChG^) z*q|Q2*W0kY+^i-e5l>`}jgG^zpv2kGLf+wzflj@H9aiY0Tpc`_otNG&=#FeMs_Rvk z+9&36QnT#*Fm)eL7-zH&v@fcMpn5_28XQPmE<2ao?RLN4hdxF3GRGTwggg&ZvO}6` oC%mhx)7!$^)2FsIeteJM4=9genX?&?VE_OC07*qoM6N<$f_@u2jQ{`u diff --git a/assets/icons/Passport/passport_DB_sfw.png b/assets/icons/Passport/passport_DB_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..69b2ac9ad4568b6f1cdc9d38f2bedb6e5d71de6d GIT binary patch literal 750 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 literal 0 HcmV?d00001 diff --git a/assets/icons/Passport/passport_happy1_46x49_sfw.png b/assets/icons/Passport/passport_happy1_46x49_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..56ea000cd03bf445ff64ac2a9c01318a4ed280e3 GIT binary patch literal 1296 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 MT&7n)()IlKe@XVQIsgCw literal 0 HcmV?d00001 diff --git a/assets/icons/Passport/passport_okay_46x49.png b/assets/icons/Passport/passport_okay2_46x49_sfw.png similarity index 100% rename from assets/icons/Passport/passport_okay_46x49.png rename to assets/icons/Passport/passport_okay2_46x49_sfw.png diff --git a/assets/icons/Passport/passport_okay3_46x49_sfw.png b/assets/icons/Passport/passport_okay3_46x49_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..e65da5b0e586ab706b40263b637f0fd3647ac5d6 GIT binary patch literal 1304 zcmaJ=eQ4Zd7*FY#?Xc({wr<5?O#hgI`A#mEaJRF)-1W}xYPsFqxo)2Mc+-pRC25j+ z*P{+~I(14dBFvRBN2hbJDTpFM)qUugbz-}TilX~M+(1Ul>R)5DzSs8Z{wND1@AE!+ ze$VrJzFzO@Y;Rc8yaqv#hGarc!;^q7T~`agZ(r^0fX6yNmi0H=hF?@%fVAqi3eaRh z=>ch==!5+eAcP>bFPoXHpG|F&G`m136&s>da3C5%LXncAXuZHkRnTKvVQhN*IEI>f z7~9IHsFV{0c{5RV!RB&jMl1JfqK-v2qM?!m9Tb4Cprt~eP5w%}KSaz#yI=z@vg z8^#t%Wm8>f)OGRp)0DQsp_LBLoqZ-aU{**6eY2u#Bu1-gTZjFZb)f4 zz7!1J!dTw-9f>51#UfE;3ES-;YGU z4KZ4(U|7662dWrCHqsNW3A>{7$+k970G6MW!x;QS=%y|i6i|6y)o~Rt0?x6bhATY9 zCt(kQeA6&js6BX*w1Sa(rB7TZJgk#$$1&t%^-rp08LMsKz#7 z>?JIBN30Zef#Tb4#lx?BI(#Kl}VE zr>*Xj7oPuOCYk$s)Y#tsK*%~np8RyS?!L1%zI<}}!MWYx!_m5O^Oebw6XCJ(_ObK- zTzE2ZDn{m<55rd*dxnMr?}ze}_QjcOWc_;&d9i~N9qGpsZ_J!4w`@NTS~`yv?;ke? zhv>V?2d>v2-`~IQ&nd6F)duN|MblK>4PVZ`*vyPb!yT5yrS%35K zA7@kco=YdXx2-A%Zb=_}`+d*RzhdRBuUsVFjp#>D|NZ4t)}y$rkd*-G80G55pQ2+n{ literal 0 HcmV?d00001 diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61.png b/assets/icons/RFID/RFIDDolphinReceive_97x61.png index e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083..2528ebc95d79335975511a384c70c010d476a8c0 100644 GIT binary patch literal 4862 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 literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083 GIT binary patch literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ literal 0 HcmV?d00001 diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61.png b/assets/icons/RFID/RFIDDolphinSend_97x61.png index 380a970d9004cba5520560fd9aa24aa42924e2a1..fef503263fd962534e7e5543954612bf6c1faefd 100644 GIT binary patch literal 4882 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 literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..380a970d9004cba5520560fd9aa24aa42924e2a1 GIT binary patch literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ literal 0 HcmV?d00001 diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57.png index 34199910945376f054daa0c1738d7e64dc410421..78c4e93f8a8c4e94eb0513c7b59f6dde68748205 100644 GIT binary patch literal 4466 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 Ef`SCamH+?% literal 2681 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 M07*qoM6N<$g8AuxBLDyZ literal 3898 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 T?I80e8v&Rb+u+JDu3`TM+ZHm< literal 0 HcmV?d00001 diff --git a/assets/icons/StatusBar/Attention_5x8.png b/assets/icons/StatusBar/Attention_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..137d4c4d054227f27ad6a7e3e374b100d975d9af GIT binary patch literal 1690 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 literal 0 HcmV?d00001 diff --git a/assets/icons/SubGhz/Scanning_123x52.png b/assets/icons/SubGhz/Scanning_123x52.png index ec785948d035b717944bc41811f73d190e6fc3bd..a48c5330e85c2135866fe99bcb6f3534d06c6d53 100644 GIT binary patch literal 4092 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 N!3~Mzlm%~0{|8*@s)qmo diff --git a/assets/icons/SubGhz/Scanning_123x52_sfw.png b/assets/icons/SubGhz/Scanning_123x52_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..ec785948d035b717944bc41811f73d190e6fc3bd GIT binary patch literal 1690 zcmaJ?X;2eq7>)>*yJ)S*p|h?~Q6$-f1d>2NNH}5%Lt|z{KrzQc(y-YyStJQ+g^C9Q zSWv-YttjHfh@wyt@GLFhMTjB}M;LSv6%;{^;@J%X_DAW??0&~Q&+|U-`@P@n?x@Hx zJ8Nfa0)b%14d?LjF%^HQmS*_Zy;Prz4^CJ}G`0p!z*2-Nm=GjEMKHicgo!X87D}`~ zG{XJ_!fZF0AR3G2MKHxELKK=XL=B?E*#v@rphhVa%V7)_o@3{?OoMWF~y##kV3^-~Ura#~iQo~#pIF_K28B$0`bDW@qQkN5vj1er#wF+Tj+ z?|%xb1zIIc;=^h*StZ6#E@7!Dl#l0+R;G}k zDeC1D1RjscRj4tcLJV^`ED)C<%48Czw=bJb<4}SjatMt~4q?;jbe~|z8=_EsXfzI; zGR5Vf;$#F?U{hSlXD)k2uBjOiB_5drt7MyCNvH}%fQg)$vYEXwX4ISHN@n&FG$WUU zn<1G__FpGGwS~8jX*%7w_+q;CVFljrD!j4V;}c)t_r;dW2@+`9`eU1O>Hy1H=Z_x? z#)vXQ4`HsunYK6jxY4-M*|zlR`rg;$+V*St8!R`}`J(H(M(Fn4S4n;$pnW-UN$QA` z?fY?KM*pGHedF?8-QC`CtG>sHp2-iZetB?^`cj>4f5g#9o;N06J$3^rWaoX6Zp!iN6AS8ml(v%micb;q2O2KuEfM&;;GfOLnbEbQ;Xh2MB~uUb$O z=Tf34Z;Mngv^s8}xvK?|Q)X5xaeMf&yLz=D);7(;s_;xKaAkeDy_0K?%Yn2|k9C6T z^kdg4x7}!2l%F~e-2N&yKK{I*<bm{PN1M%~EqIS+ z#{g%nih7mt;>v=&E{mA(e4W(c;<>|5`bM6gWBHY@vRSt9LE+^Uhns6zS!2UzZw(cU zoUfWE)n;PLGTwze=xNtR#E5s54 z?u4@P3{ljfm#4cr?==i}&Q~nx+6o_SALMl>7g+8+b*EGr+g0b>QusCdoqSG@XTkT} zJKW>*p7M7!ScsC+I$SWe`PeH**g7LKd+2^e4BT{9(i5Fx*_t#~^L&3q*^<`Bg8SLL zJr^&8Wvz+nlWyNPFyz7qEt>zJmbmY7f19~I|Kz}R|I(kU_SSdG*|X+`TiP_=YR)O9 z%>w2`t#*GaxqGd${KiVYrAK$bi$_|DyT^Fr>F$>+;8w9Tw&Z$d+!FWSfdT)vdK%bz zZxc14Zjp1X%kW^EaVRy?Y1r_3XHB46)J>z~?!08J+0&8}hNJx?^62efuWTu{t@zFL zVZ4jin2pbcGJ}2f!HjR`wC}%p=#A$7DVOR4RjdwPz~ZWrAMHE3V&KiltB9(rl{Yu) zpLzA}yq2xqYW6IxKLj-^F^5Q^3s;lX5 N!3~Mzlm%~0{|8*@s)qmo literal 0 HcmV?d00001 diff --git a/assets/icons/U2F/Auth_62x31.png b/assets/icons/U2F/Auth_62x31.png index 40f094ac9ba758ea06838c2bb8376cf6f28f8050..dd220bb65104666cb19752a96fe716cc44dffb68 100644 GIT binary patch delta 1860 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 diff --git a/assets/icons/U2F/Auth_62x31_sfw.png b/assets/icons/U2F/Auth_62x31_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..40f094ac9ba758ea06838c2bb8376cf6f28f8050 GIT binary patch literal 3761 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 literal 0 HcmV?d00001 diff --git a/assets/icons/U2F/Connect_me_62x31.png b/assets/icons/U2F/Connect_me_62x31.png index 68c48c0e68142548919d6a4b02e40b48a243b04e..495e8ab55c1e2dabb547151c2319f321f4af13bb 100644 GIT binary patch delta 1891 zcmV-p2b}n~9p?^^8Gix*001yk>3{$L010qNS#tmY4#NNd4#NS*Z>VGd00$XKL_t(| z+O?QlOjSn|hG8oRh>GGZ;swMCiVCP$2m(U5h>bOddI{B*1Oy&HNv)*;Bx*FKeZkhI zB-%dM1e>&xrj7K$)`!xj)v9R=_C!b_pdvRBLEK z*T2@F<>ch7UAs0lHPyw%W%1(0vu4fWapugKPEJnFFPgqDO&e_bx7Dv2om?gSDrp=gE^NdIBg8sLrZYs~}Qxax$H$sHm{8u)Msypr9a30==5a zBGSKg>sDb`!T3UNZ*QM6WyTNU-=G)7 zYFilOUQ<&uzR)2$tR2p2fv%WSS69dWba!{Vy1F(sHO-wnH}-0rkB?7qa4qP>Z+C&CQU9LAEP4HkJ-sfu~_V z#xQ|tn194ZjmD`;NvwJutRN6S306k%AA?JmE}b)Hj-En*yuA3Bc%7INGuVyUvu8hk{8$kQU-Am( z?A*E2_gg=;M0|WaQV3$W#INShpO3-F6#Y?pU4PvzLq~%F#K&+OlmP3qXU}jjzI^!b zA$^&LNz6p`=|o0Gp7kkvzjc3J)4r_#GQ-2enNI-Rym|B5wQC_EAp|r(ApnMEhL_if z=x!A{+yE273SaRP(sAn4DUvI=MK`erNnvN0jbX=+A4ea3eSO!hTNfG{x^?T;O`A3y zK7V`|BZc~%ot>gEBxXHs!d_jr(F4~kaauG$x8h zK>$3BB_uB%OG`^3dQnl)>C>m*ZA;r&o$Oz}hCH|~m=%8D%x3@Q9 zpPQQt{n)^a&OM4J` z;6QeEHXMujbHnhp3SEo72>3GouLSlGo6QwtC}J}Ty$W=pIQU=-noS;AxpHMfLIO6d z|0623>1_xD6DU`hom90k;qSx~KA+fL6xN~fX1L9`J_cS#1c6SZQuHAa7cN{VDJcPc zMMZ_ofH~|5l2?`VkAsU6B)gh-TYnW0=P}|s&?V!b*^*Pr%F2!%I|jLB1}uWRk-x8( znRZ*{>%ZWIQQ~%}hJg%x34G)~qysTQ4ats3d#UH=KL%AGc38#KfFA-K`Re)e=T#yz zqqu{(Rq3Mc7IUgb{Pp(s!U{M8ttp}KG4_}W?!rRu;MPNN1iG9;%oJvP5=&i3>3!?xNjc8YH;|2Z^PqxcysetUaZ|03GL9}00m)2BnQ5|^li z6l6#^jwh8`gsZNBk4%#(X<}kxEO5X$;u=O6-_c_oI#~I0_wHSMOqimNkej+dk>p%> zIRQXX@R|Eb!yEtVe+*o8@qZZhjj4!Vwrp8jTbnIF4l~|P@K<-eW^aB&TOHKn)K_}uPy*Tkbhhb?3!P=Xj2!XFf!3Ut%seSt+u^c>?GJv9N2wf(2uK d6tdy_{{j6OA(WAZ17rXI002ovPDHLkV1l_ti)jD= literal 3767 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 aN(~_Yif=!tN`^N*QF&1S0K}{@ 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 IM6N<$g6Xe=bpQYW literal 3765 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 literal 3751 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 W#^eyOwOvyH0000ZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj diff --git a/assets/icons/iButton/DolphinMafia_115x62_sfw.png b/assets/icons/iButton/DolphinMafia_115x62_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..66fdb40ff2651916faed4a2ae1d564cafdbf7bcb GIT binary patch literal 2504 zcmbVO3se(V8ji5Kg5ZmV3I#ewZHbsZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj literal 0 HcmV?d00001 diff --git a/assets/icons/iButton/DolphinNice_96x59.png b/assets/icons/iButton/DolphinNice_96x59.png index a299d3630239b4486e249cc501872bed5996df3b..43cc58bd9b263ddf1ae1676f997ebd3aa5c2d74d 100644 GIT binary patch literal 5422 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 Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QM|5y* 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 literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S diff --git a/assets/icons/iButton/DolphinWait_61x59_sfw.png b/assets/icons/iButton/DolphinWait_61x59_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..423e079199b00df0d910981caf8944cbaf8ee67e GIT binary patch literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S literal 0 HcmV?d00001 diff --git a/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png index 2b4bec7c6f14f53e7362da75f95b83bf387eee54..90b589ff8e9f18b28d7fd75ca525a1db24381b06 100644 GIT binary patch literal 4719 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 literal 2157 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 Og3&$z^CF>$-U|nYD9Y9V diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm deleted file mode 100644 index 843aed27c7df836480f795cd649be62ae08f6ad7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 462 zcmV;<0Wtmo0LlRXAM^hM2j_qv0g(b1{{QfUgO`ENzlTmAfCsnZ-#-zY1_2(x1JnQa zpU5x);RFmIj^FtF!@x2kY<}bOkKlNqkOKjVL_Smh@bq91ZU9h)#t$9%xiAHCfP4Yv z4+w}p2f`0C(u2r+VDcXj0EDD2FnHX}OCATj#2%sY^Mlc{!Sdh((UQROC#Zfw>RuDt z1IfA|^(CAEUSRdWLFPsE2dEfebKv`bf$6}5#{v1^gO~3?8ra`UB*?AfosK+`caK2f!Rd;?FR7|AFNLCzHIu;ty~D#(+b>2gW@gg~5r^p_{(+8!2 zeqix8mI*jc?Ad%Ohh0Y E96sC7hyVZp diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm deleted file mode 100644 index ad87a307189f29491e693d9665e9c9780584f1ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 487 zcmV={XPjp2dR8PL=T9&y+VlZIXEbZ<-iBL z44zYPDBT$Ej}Ouzy-{_a2$C2Pi7B@|AP+=2i*PsFpzrS0qC$N7=!Kt4?_8H z0qQw0eV0rgo&b7_Odgg7d4t8`N%fc~%pO0|0hYuHAolr?43;7Rj1SiiKrncLMi>uR z|M~(C83+C$0K)^$2minR1_dzw1qA+p@&E8lC2+h0#QuQt-~It*I8U%Vzwje){3LGn d2b!qLWdJ{zJg?x9RE-24Ux0h3MTmq0fq*|E+=BoB diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm deleted file mode 100644 index 78de0389efdf70991b33b41f855675bea8b3248f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 483 zcmV<90UZ7T0N()sAH)9#2j&C+0|d8o{sb@o|Dqpm2R{BSIQ#;94`1h=egq%zw5Nl1 z`^VxO0P#Ss1$O<%~y{_3%OC!1`{OEX-yP zQhHzvXZ#1O5I$T0`GeekTmbzvOdkMpz(+y(V1P|Q_Jk-!DKs!{!ec`g{ZOUl37v0q-|=uK*tKF!>Y7h=BD4 z2z-I`02$B>f$@h=hycOnUN{Uqek8L8#DBquhJ)7t4@>=r01w51G|mhUhzLCc<-iB2 zB*FIiFnV|a>wgEmfgWJ-0SGv-2r`%h*YZFz*scaJFJBn|$zmW7dqc0&^`H0=&I6WuBKln$G z=wxsXF};rBq8>R^h+^+CK)`>)vBQJs|5bqAA@F1=C(s^Wa7e&m#NmIaJb^<%HH>&3 ZO7jPmd=ycL!Vv-Jj~oCzVGTnElpL}U-NOI? diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm deleted file mode 100644 index 32bb893cae287bcce9d3d5d3590bc1cd890ac96d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 488 zcmV%HfP*6h0v~`pVBx3d-@t?Z0gd<`-w!SG~&;P*%1`-&!f#u&QiD1B{cp!M5 z*T4@k@dHIRya0Lsf#>%$s64y?;s=b)P_gmygT^usf5(U(kdXWvj2uS;QYP-^ZX0iFb`n)vCn&d56B#B5Dl;X{tu)0%s^rfiTZqiV*vP%+!iA71(1FT zLqLEd%kUq6`Tarm&olf3#r`v3c_qXiqwzP4iGD!$i@VewpWt~w!{k3M06t0LA1nYq zU?TGm%s#^afdk484pY=(jldH;hC4F}W*|6#yFMeBeEtp@Uy@Oi7wz`rnfxJVwz^9PcMUFR@>OabfjRUhO21sK5naO49W30Ohx5C1Zf{{Y4- z)Bj)m3WXF@cn1(3zw@}jcpeg|cmeDz9RD01DFH{IJg}hw!|=pnzfgI2!9xJSnG7D^ e_#h}?$3+LG{sBOMK_QAm2b3Hhun0grhzAGqspMh+ diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm deleted file mode 100644 index d39d7c7095b350f6fe85554e7246e86cb2d6a0f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm deleted file mode 100644 index c3bea7837cc3042b11306dc8c2a4b2e122cc786b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm deleted file mode 100644 index 83fb1ac60079bf92019ad0610c834fe7e662d02a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm deleted file mode 100644 index feb5ab082ec1d3388d93a6ea6290666f20dcd358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm deleted file mode 100644 index d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm deleted file mode 100644 index b4a87c26bb2d04806872f9dbd425a6c92a58db9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm deleted file mode 100644 index b73177b87d59022dd64c4bdc1dfb43378fadddcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm deleted file mode 100644 index feb5ab082ec1d3388d93a6ea6290666f20dcd358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm deleted file mode 100644 index d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm deleted file mode 100644 index b4a87c26bb2d04806872f9dbd425a6c92a58db9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm deleted file mode 100644 index b73177b87d59022dd64c4bdc1dfb43378fadddcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm deleted file mode 100644 index d39d7c7095b350f6fe85554e7246e86cb2d6a0f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm deleted file mode 100644 index c3bea7837cc3042b11306dc8c2a4b2e122cc786b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm deleted file mode 100644 index 83fb1ac60079bf92019ad0610c834fe7e662d02a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm deleted file mode 100644 index d39d7c7095b350f6fe85554e7246e86cb2d6a0f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm deleted file mode 100644 index c3bea7837cc3042b11306dc8c2a4b2e122cc786b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm deleted file mode 100644 index 83fb1ac60079bf92019ad0610c834fe7e662d02a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm deleted file mode 100644 index feb5ab082ec1d3388d93a6ea6290666f20dcd358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm deleted file mode 100644 index d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm deleted file mode 100644 index b4a87c26bb2d04806872f9dbd425a6c92a58db9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm deleted file mode 100644 index b73177b87d59022dd64c4bdc1dfb43378fadddcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm deleted file mode 100644 index feb5ab082ec1d3388d93a6ea6290666f20dcd358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L*8cZ?uu=dgz6zc1zuF%k=stJ-|M&0z-3QJ0`%nLm?|L6C z_`Ze%@BfSh0WzEIf9J~&*aN^G0PqKZJOTJ{e?$L({j2zc{Qw8-k1O;C=!g zFd6`S;As%56qNx{OcsI=LJ)lDVh~9drbG{zJrf{eFYrDx@dzzbB|rzv4h0~9!6IMK zd{yZXnq-E6z?d8dK_J9Jf%6l}K%qk+XiW|QNT5_8LHUH~U{F{tqwqhN1fmg;fqa4S z0m)#nl1hMnWV8uILV<$#2j(k*g+L`fpnOGW5=HpX1LFY#h><99C`=X;CGg+aCGQ8E zM?pZMP$(Wv_^_+Wb@k zK|s-R0pdSs`~xUy7*|NY5&cXq5TXXf*w If>Zeg7;o0wEC2ui diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta deleted file mode 100644 index 485a421b6e4585f9e7e41e4cc628e815074fdb04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16 ScmZo*U|?_nVs;=_0Ac_O$N=a7 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx b/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx deleted file mode 100644 index 4ab6aef41bfadab1eefbfc91a98cb0a2f4022f8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 480 zcmV<60U!Q=0000$000010Mr2hcmu#50PqKZEB^xrWT2jv0*4hQsbACd?J@E?;1 z1<)Un2nFCDx(RqcV}lU~5=q5DCZP77w@&A|B)s zeL(S$I0b+oP$)1EW5D`{1IN(^2hRe12cQrHA76+b0DvI+*Fd-c{EZt%0tYYn5Woe1 zP6#~w5DV~uprG*=lMrwWMxg>l0#Lw734~I37#>0#1rm|Kg(<%WJXgUkpx!eEU?2Qk<{N5=r5Kr z2v8Vd5@{5I9tJ3!ifIW06hC{I0%#mLlg~<0u@(_O97=whUF!(&@kgRKk6h@3_O1aE}9J(1`hS%#>POy z-Q00TXaBSg;sx*U0@hy X-4EY3s0JCQga3)P0fzCy|Kj-|K{|=G diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx deleted file mode 100644 index 9dc1e5dcf52f7721ffe6f002905111825fcda8c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 513 zcmXBGT}V@50LJm>eb0N&&YXj@wdq<)XCKB~5p$#$7P-|{+sv4;XvvDx=wj|7L?{S? zj=9R3Nr^%3na+9@>m$&mErIrq%OKhit56PKw(XK;Lql==>?x%R23Ol z_~>Oh)a@+MvyHN9R@1r-y9aG3GqF>mkw6yHmn2!$Qb;t}K-<4z#w$XddxVvG8??+i zG8R$OK6zlX*iFO5K1lY0N;=~(*WLwbi^DhGz&}l-;bsSd27MfIt$Rn_pdiY!!sk?4 zw-8EvX}Pz=288kc(B?BndB!jA9ug=YMy9zAC{bl8wvbusbS zO+u7rHbK`Qf0daOyXu6wnNAE5Rh^Y-%bgg+Xb*j|$eHm*B6IgyAiU2^)^bgbM#V;+ zmxN@iXPESi8n-&)Hqts$(`S|P@IJ70Tl{0JYef#|lX>Cj!+>_YF2@$8g9MM?3xVDN zTCl#OT8z})IJvA-3uM1Cc8h8?T>rV>5l%4*BcppFTwI@Wy8S1(zaM_YqPs%8aELi> t8{fhUX>fy1fgniNxeJ&S#V{idsF0~dO*nPBe7?N;id8D}o7d+nx__kHlc)dy diff --git a/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx deleted file mode 100644 index 03c6304a2d4d334ac519d1861981fcfd0393755f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 375 zcmV--0f_!D0000z00000K&2#AN(I0uRnb%ia8*gFk_?zCl}e=wSXHT1RT->OB_&ld zR;pB~N^O=Zsj5SS@@HBmk^dq#ytRuT=U801$K+RRa+K zaBk`XfJD#@t&ac@bT?`dNCaG8ts??}|8-Bt0AK-Iwap-1KyNLN3kvvJTMfkluQs+y zf8bSZT@3)JYBg&Q33{qpo=gDDv^1*@K&q%}tqKUlS~XjZ05DW-t`0!zvs5b$60j*f Vt&RWy*=k!20Kl|ft_px;u4-Myk9+_C diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx deleted file mode 100644 index 8af359b940e9a14587bb8ae598aeff8e53b6542a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmZo*U|?_nVnzlb#%uNaA8<5O*ng-ku`!k4;5o#|^Znq_;{W{zl;wX&{8nT5x4|L7 z;m-p%=id#Nn*Ycj`mg?t;r@Z|{|si^i{!~gt0>JR^u|M0*4X8pte z@-PmH43zU<{sYtF!*BnvZ>zC8@B^gpzg+Y5@3#B``oC=IV+w5de*eLCI8b`#gFl5I zc76PB_{U{&aMyu~V@doza((q1rsO$4e#qW&=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx deleted file mode 100644 index 715815f0cad52a4138cb8ff3db6a9fa15e4a2b46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx deleted file mode 100644 index 715815f0cad52a4138cb8ff3db6a9fa15e4a2b46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx deleted file mode 100644 index cb92216c4e5943d4d845c979c7d8c59df040b95b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUyG$`6BHT)5{U3WANw^bz!@%V0w)Q;`Zx7& zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H7W%wg diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx deleted file mode 100644 index 8932c5ed3ba499c5a9259d2d228330e5f4a5e94d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUkjyb6BHT)5{U3WAN#c`z!@%V0w)Q;`ZxA( zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H5-7Y~ diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx deleted file mode 100644 index 3eefd86bc994996073b05eee2f627dba8b8bb9cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 502 zcmV z&sG8a-`8rskx5wK7)>Us0pP#(ZleJPjN~>tnxu#E->Y)$fYOw}IF(JzlkvaqZm59L zn!q(3O%<2%+Ur?tK+={#D4LqLCjC;k%6NwJ4USW(=8s@H*J~&=g(F0i>akI}1lB2G z29#7bHL8bJFJPO-b|BIzjS`7K#=-O%tW1C!M1j;(FcPR7!&yw2Ez*HV7Yqg=LaI~& zsicZPB~S=J3}pb+q)`E64ImJP(n$co14jWCf(_P~1R!X_pR1rP93p`O2CV>wAkqb> zY=+_ussN<{uuL&bhVa-R0*C`%KnXG~hT{Pe2H0%?W&lvRkz@%4I0nT)8g&3`(G6!V z8{X0a4S?BdfsX>z14;}HcpX8aX?KFr3IhRwgI)=cf_y4aB`d}%kV=M}r1S}a;HwN6 zph_Sw)HbgIK>`NU0#X~Lki}qD0BlSk6KD-E)SclLKKZ5N}US)0ty8g z!LX4heZasK>JQHga$BQC_!{%pQ?BqK(GqQC;$Ke diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx deleted file mode 100644 index 22a76dd4daaa01bda8be01f522bf58c0a9e22e2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 352 zcmV-m0iXUi0000q0000108|0&f4~O;{CmL10@edpx%UGcAJ`2@FYpYIKkykK|6myQ zf+Gh8mv8h2PuJ7`4+<8n3?S3C?LY9)xc~3I5AZbZzE3Z@uhZZ zPz|IH-D|$@AQ%W1@>|%TEd~Sqk?t@M3`7RqN1OqM5dr>prx*}vUuXA#(BdE+%j{qb zJV*!k?{NbU02)8(xETMR+sLGR5f1m>b_O75{_c1R1wsS7|IC4-*noew_dE>((E+}D z?)X>*aF{>2oEibnfL}lNd*lNi4Ih2i@q*UCKO5^mgT4(f{NG9cqrktL+dc#TmVtgh y_WcKc4on-}yc#iZpo2$&-=jf4AA9x#`V=qyv>Gt@C-d+B;D17bMIPUS{uB^jDyGK( diff --git a/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx deleted file mode 100644 index 75f57367cf539f9f075e06c983de83f6fb826cbd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 458 zcmV;*0X6=60000q000010KNgL>v~p-wblX;ingkga@+Z!VEF3Vy1h-Nsss;>WlLjT z+vSDxD$@V=`)#Pcbv>>9|7#Wx&5Nt|-|OO_wXk3SV@c)v1LO}`1~3LaECxm|c7+Gq z2m>6h2L%U~gW5raz`%6S3p+Y|APS-Q zK##A$oB+fgJ^=WG-rxfrlnVe4j0c1gI%d$oTd#n@lgCjD5;hNsL<$U^9wc}l9f%YX0SK{8!Osw=LDNGFxR2stpn?O& z4e&3=BFhB=4Gy_F7sql89~ci(C=ZJS76saX%cTJLz+kXFCR diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx deleted file mode 100644 index 4a569c1eb27507a1301fa34d2ec5d96d62fe43be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tYz`Jw>Mf_uuaow2G_xpSDq; zYo|#3>Y%M&TBU-sCA_pIg(RtNzcnQ&bm13Kd)b9kJiK12zMi3FI8`f)^MACLpQdN3 z=)KufA{GS|X?~AhIooSW3e$h-uWP0ReU-W&JV|S%%1@Q@+Zy4Mync!Pn|*w;)=D4c zdgFhqTqaEuyBGh*d!?2x_y6g;CVMUVBl<7?uj(X`k4pPP|E*rK%8T>={%uROCQi`; E05YU{Gynhq diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx deleted file mode 100644 index 582247d43e0c1cfbed6e9d8061ec26b7ad417339..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmV+c0sj6z0000V00000`~UwxNs=T9{r~^Jk|aqI|Ns9#RaI3L|Nr-YBuSDa|3CGA zNs=T9|NrKHRaJcz|G&$Bk|b3r{eRPaB}r}t|NW(FNR?D7|G(LLtEB!2y?!8TBq@^> zm;X<{N>ZjGd;TjjQKhOBU#n8JN{Y%Qn;1$el2nokx7w6aRgu;xKOLczNUDk@>zFDr zl@k97|658w@~{~PtJl!ST;-&2w*DXEkcciSjeQYtwq z{+q{>ic(bte>487R7FxM-e3Gnk*Jg;|ChRxDiyT~{$KqiQleB8zg7OLL{*t0|G&1S HDoL^wQy6*i diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx deleted file mode 100644 index fae05bb57f5b883c6fd532a150899d556ce750cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tY!bdWlT`@4w$GXd+kjKW(GH zrK?5aR|jqN(oz+iE#aj#sl;1#`>iQKp$q?U*~>1R;<0L)$m91?1y!xebKX{VX$}fjR%WrFhPjdRI@^AL>$y%W& zx$2Got#T;|61*4x$9v_Y{aXL0@0#pY8p-l6{;%q!9YLD=L;tN-37x|AfB&|nt0q}# F0ssSEetQ4_ diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx deleted file mode 100644 index c91714b3f1114d842e45cb614c456d3b40b164c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCV9C983fU@}>T=Wn`|tM(TCZ3APupl> zSB!Z4>Y%NwLUw7-mhjS=wCD@#_FGeeR7JmP*~>1SvSP(2(bqG)LRD3FD*un3G9|<- zj{n|lrO3`H>-4@yubmaNQe5`Gw2+pT>uRa{!ArwDwNy3BZ*xRXTH+z}Px|`gRY0xv zoBwHf3VCwfi~r+2X~`sw|I>E`FV$Kq^e_IOs;1`@mHkuyczXqD0^PYUbmf#FP5?Ek BcEJDu diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx deleted file mode 100644 index c37cd533d7a3df21e738cf4cbffb4483f9a551ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 684 zcmV;d0#p5S0000!000010H6YZLN^}%?5|1(7!WzTpQ}}`Euew}ngP5Hj;~c2fP$jGr)v<6eb8Gs)sl8j*#s@XScl6&=tNY&k92~!j|6}!C%F({S z=Im!~n^}Lh8}|n{qCf2YTIbN;d^{Y!$luc2+tfmT;O6p2{@$+s;u0Nyq&ubU+S+43 z;Opc1T&ewrC;o+lm+g+}cjy1GaL2*R;^lwxR{!*0y&eZI%bknDfA}9@>-@Qxxdguh zHt$7&%lhSFp*QRvm%JTsk2Vzmw?QSo;B$U?kf={G8LRhAgRRMeLZOk0*ZO`PU~tAW zGY*xyZVq0LW(g9^P&}V`@Hu-mpY+pX;Q!sljoGOGruHw-|JDv}PtX5f!E=B2vD)AP z#ix>f|Mpw>b$Pyj<-U{q@850V)#iKul=@fOKerC90~!AOL;9bN{p=jrZnvfZu;g-g zRp4^vK9Gnff+LLY|G~?N_C_Fh2_GAK{tmaJ;{kwixP9R3K|27^fI>VQ;B!Z_5HK-V zHR8sKP65mN2PXnGet`jtLErm~$U0E(#1IT!4Zr*xvG4R83}26Ub!ZUZ{*Qr+{oe;HgCGB&fs6m!4z_>( ztu6=ux*dOd0K6ane|-FK3V^VG;DP*q{h;O83ka4!;OO^3$=7KuaoHhW1k0s;3Y7vSDM*2jpjMLZBI+Z4XW{hX{Mk{Be^YK@J5q) zMh*$0wO$8l8=yrnoQmn700Rn)+GZo12Eue7;L~08>hhygRXatT! z5V%2MQ|#Y9&oc{*PGg~rLZ;cyZI~7~7?py-2L+C779>Hy$m|%5R4_(dd5!}iVzNvi zFsbB!9t$iDlR;&G(+wrSHI4(FQq1J_fU>v($AQ|K>7?1j8saoG??ro}3r+_Dp#c8y z@RePXLVLP>;p~ z1<-;%L;z7qL!HGRAvc6clAzh^h+u>z+PQZ&$1fdia9P4Fe%-@6W<@^19;PE{V0Oz>C%GhgJ z`+8s{;n_2uD_3<3AA+l6e#e32@NT8_TotcCe!)e;kJ<6k^Z%#h207tugoa(l243*s|ZbVAbYge|i#h@7nr; zp9<{WL}jZ7*6J%9UFd64^bSU=JrHKUlHPcbinq^Xl%^-b<_#RJ^*~y(=ahV36X1F# McGibf>8G#$2UW|uRR910 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx deleted file mode 100644 index 47b611a6ad2b3c2fa21cda23b155e56742d70c77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 482 zcmV<80UiEq0000q000010M-Gj>v~p-wblX+YPPDAa@+Z!VAkr|y1h-Nsss&fWlLjT z+vSBXD$@V=`)#PD^*ycq|7#Wvy^E{&-|OP07o_t2fwBg_O97FK-Jw9&%HVKNd1y8? z&PSZtNcj%R8vp bytes: return data -def pack_anim(src: pathlib.Path, dst: pathlib.Path): - if not (src / "meta.txt").is_file(): - return - dst.mkdir(parents=True, exist_ok=True) - for frame in src.iterdir(): - if not frame.is_file(): - continue - if frame.name == "meta.txt": - shutil.copyfile(src / "meta.txt", dst / "meta.txt") - continue - elif frame.name.startswith("frame_"): - (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) - - -def pack_icon_animated(src: pathlib.Path, dst: pathlib.Path): - if not (src / "frame_rate").is_file(): - return - dst.mkdir(parents=True, exist_ok=True) - frame_count = 0 - frame_rate = None - size = None - for frame in src.iterdir(): - if not frame.is_file(): - continue - if frame.name == "frame_rate": - frame_rate = int((src / "frame_rate").read_text()) - continue - elif frame.name.startswith("frame_"): - frame_count += 1 - if not size: - size = Image.open(frame).size - (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) - (dst / "meta").write_bytes(struct.pack(" Date: Fri, 10 Feb 2023 21:16:40 +0100 Subject: [PATCH 4/5] More subghz (btw.. entire subghz update, credits to unleashed. will be in release note too) --- firmware/targets/f7/api_symbols.csv | 6 + lib/subghz/protocols/alutech_at_4n.c | 303 +++++++++++++++++++++++---- lib/subghz/protocols/alutech_at_4n.h | 56 ++++- 3 files changed, 328 insertions(+), 37 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index cbe0ec2ed..d229192fd 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2679,6 +2679,7 @@ Function,-,subghz_keystore_load,_Bool,"SubGhzKeystore*, const char*" Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*" Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" +Function,-,subghz_protocol_alutech_at_4n_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" Function,+,subghz_protocol_blocks_add_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_add_to_128_bit,void,"SubGhzBlockDecoder*, uint8_t, uint64_t*" @@ -3039,6 +3040,11 @@ Function,-,subghz_protocol_decoder_star_line_free,void,void* Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_star_line_reset,void,void* +Function,-,subghz_protocol_encoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_alutech_at_4n_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_alutech_at_4n_free,void,void* +Function,-,subghz_protocol_encoder_alutech_at_4n_stop,void,void* +Function,-,subghz_protocol_encoder_alutech_at_4n_yield,LevelDuration,void* Function,-,subghz_protocol_decoder_star_line_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index 6bcf9f25d..7849f7b21 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -34,6 +34,8 @@ struct SubGhzProtocolEncoderAlutech_at_4n { SubGhzProtocolBlockEncoder encoder; SubGhzBlockGeneric generic; + const char* alutech_at_4n_rainbow_table_file_name; + uint32_t crc; }; typedef enum { @@ -57,23 +59,77 @@ const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = { }; const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = { - .alloc = NULL, - .free = NULL, + .alloc = subghz_protocol_encoder_alutech_at_4n_alloc, + .free = subghz_protocol_encoder_alutech_at_4n_free, - .deserialize = NULL, - .stop = NULL, - .yield = NULL, + .deserialize = subghz_protocol_encoder_alutech_at_4n_deserialize, + .stop = subghz_protocol_encoder_alutech_at_4n_stop, + .yield = subghz_protocol_encoder_alutech_at_4n_yield, }; const SubGhzProtocol subghz_protocol_alutech_at_4n = { .name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME, .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, .decoder = &subghz_protocol_alutech_at_4n_decoder, .encoder = &subghz_protocol_alutech_at_4n_encoder, }; +void* subghz_protocol_encoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderAlutech_at_4n* instance = + malloc(sizeof(SubGhzProtocolEncoderAlutech_at_4n)); + + instance->base.protocol = &subghz_protocol_alutech_at_4n; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + instance->alutech_at_4n_rainbow_table_file_name = + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment); + if(instance->alutech_at_4n_rainbow_table_file_name) { + FURI_LOG_I( + TAG, "Loading rainbow table from %s", instance->alutech_at_4n_rainbow_table_file_name); + } + + return instance; +} + +void subghz_protocol_encoder_alutech_at_4n_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderAlutech_at_4n* instance = context; + free(instance->encoder.upload); + free(instance); +} + +void subghz_protocol_encoder_alutech_at_4n_stop(void* context) { + SubGhzProtocolEncoderAlutech_at_4n* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_alutech_at_4n_yield(void* context) { + SubGhzProtocolEncoderAlutech_at_4n* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + /** * Read bytes from rainbow table * @param file_name Full path to rainbow table the file @@ -164,37 +220,212 @@ static uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* return data; } -// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { -// uint8_t* p = (uint8_t*)&data; -// uint32_t data1 = 0; -// uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; -// uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; -// uint32_t magic_data[] = { -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; +static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { + uint8_t* p = (uint8_t*)&data; + uint32_t data1 = 0; + uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; + uint32_t magic_data[] = { + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; -// do { -// data1 = data1 + magic_data[0]; -// data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ -// ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); -// data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ -// ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); -// } while(data1 != magic_data[5]); -// p[0] = (uint8_t)(data2 >> 24); -// p[1] = (uint8_t)(data2 >> 16); -// p[3] = (uint8_t)data2; -// p[4] = (uint8_t)(data3 >> 24); -// p[5] = (uint8_t)(data3 >> 16); -// p[2] = (uint8_t)(data2 >> 8); -// p[6] = (uint8_t)(data3 >> 8); -// p[7] = (uint8_t)data3; + do { + data1 = data1 + magic_data[0]; + data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ + ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); + data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ + ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); + } while(data1 != magic_data[5]); + p[0] = (uint8_t)(data2 >> 24); + p[1] = (uint8_t)(data2 >> 16); + p[3] = (uint8_t)data2; + p[4] = (uint8_t)(data3 >> 24); + p[5] = (uint8_t)(data3 >> 16); + p[2] = (uint8_t)(data2 >> 8); + p[6] = (uint8_t)(data3 >> 8); + p[7] = (uint8_t)data3; -// return data; -// } + return data; +} + +static bool subghz_protocol_alutech_at_4n_gen_data( + SubGhzProtocolEncoderAlutech_at_4n* instance, + uint8_t btn) { + UNUSED(btn); + + uint64_t data = subghz_protocol_blocks_reverse_key(instance->generic.data, 64); + + data = subghz_protocol_alutech_at_4n_decrypt( + data, instance->alutech_at_4n_rainbow_table_file_name); + uint8_t crc = data >> 56; + if(crc == subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF))) { + instance->generic.btn = (uint8_t)data & 0xFF; + instance->generic.cnt = (uint16_t)(data >> 8) & 0xFFFF; + instance->generic.serial = (uint32_t)(data >> 24) & 0xFFFFFFFF; + } + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + crc = subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)(instance->generic.cnt & 0xFF)); + data = (uint64_t)crc << 56 | (uint64_t)instance->generic.serial << 24 | + (uint32_t)instance->generic.cnt << 8 | instance->generic.btn; + + data = subghz_protocol_alutech_at_4n_encrypt( + data, instance->alutech_at_4n_rainbow_table_file_name); + crc = subghz_protocol_alutech_at_4n_crc(data); + instance->generic.data = subghz_protocol_blocks_reverse_key(data, 64); + instance->crc = subghz_protocol_blocks_reverse_key(crc, 8); + return true; +} + +bool subghz_protocol_alutech_at_4n_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderAlutech_at_4n* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 72; + bool res = subghz_protocol_alutech_at_4n_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_alutech_at_4n_get_upload( + SubGhzProtocolEncoderAlutech_at_4n* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_alutech_at_4n_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + // Send preambula + for(uint8_t i = 0; i < 12; ++i) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); // 0 + } + + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 9; + + // Send key data + for(uint8_t i = 64; i > 0; --i) { + if(bit_read(instance->generic.data, i - 1)) { + //1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_alutech_at_4n_const.te_long); + } else { + //0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_alutech_at_4n_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); + } + } + // Send crc + for(uint8_t i = 8; i > 0; --i) { + if(bit_read(instance->crc, i - 1)) { + //1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_alutech_at_4n_const.te_long); + } else { + //0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_alutech_at_4n_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); + } + } + // Inter-frame silence + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_alutech_at_4n_const.te_long * 20; + + size_t size_upload = index; + + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + return true; +} + +bool subghz_protocol_encoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderAlutech_at_4n* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_alutech_at_4n_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_update_uint32(flipper_format, "CRC", &instance->crc, 1)) { + FURI_LOG_E(TAG, "Unable to add CRC"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { SubGhzProtocolDecoderAlutech_at_4n* instance = @@ -452,4 +683,4 @@ void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* instance->generic.serial, instance->generic.btn, instance->generic.cnt); -} +} \ No newline at end of file diff --git a/lib/subghz/protocols/alutech_at_4n.h b/lib/subghz/protocols/alutech_at_4n.h index 38bac3ea6..b6d51439b 100644 --- a/lib/subghz/protocols/alutech_at_4n.h +++ b/lib/subghz/protocols/alutech_at_4n.h @@ -10,6 +10,60 @@ extern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder; extern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder; extern const SubGhzProtocol subghz_protocol_alutech_at_4n; +/** + * Allocate SubGhzProtocolEncoderAlutech_at_4n. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderAlutech_at_4n* pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + */ +void* subghz_protocol_encoder_alutech_at_4n_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + */ +void subghz_protocol_encoder_alutech_at_4n_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_alutech_at_4n_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + */ +void subghz_protocol_encoder_alutech_at_4n_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_alutech_at_4n_yield(void* context); + /** * Allocate SubGhzProtocolDecoderAlutech_at_4n. * @param environment Pointer to a SubGhzEnvironment instance @@ -71,4 +125,4 @@ bool subghz_protocol_decoder_alutech_at_4n_deserialize( * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance * @param output Resulting text */ -void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); \ No newline at end of file From 6555082e2aa115fc16e2bdf668bee6be1c447def Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 20:50:30 +0000 Subject: [PATCH 5/5] Revert "Merge branch 'dev' of https://github.com/ClaraCrazy/Flipper-Xtreme into dev" This reverts commit 708dd167c8c57b453978d406448c6769fccb5435. --- .gitignore | 2 + .../main/archive/helpers/archive_browser.c | 12 +- .../main/archive/helpers/archive_browser.h | 2 + .../main/archive/views/archive_browser_view.c | 1 + .../main/archive/views/archive_browser_view.h | 1 + .../main/bad_usb/scenes/bad_usb_scene_error.c | 4 +- .../main/bad_usb/views/bad_usb_view.c | 9 +- .../main/u2f/scenes/u2f_scene_error.c | 4 +- applications/main/u2f/views/u2f_view.c | 10 +- .../plugins/nightstand/application.fam | 1 - applications/plugins/pomodoro/application.fam | 2 +- .../plugins/scrambler/application.fam | 2 +- .../desktop/animations/animation_manager.c | 20 +- .../desktop/animations/animation_storage.c | 10 +- applications/services/desktop/desktop.c | 5 + .../desktop/scenes/desktop_scene_fault.c | 4 +- .../settings/dolphin_passport/passport.c | 2 +- .../scenes/power_settings_scene_power_off.c | 2 +- .../scenes/xtreme_settings_scene_start.c | 14 - .../settings/xtreme_settings/xtreme_assets.c | 282 ++++++++---------- .../settings/xtreme_settings/xtreme_assets.h | 13 +- .../xtreme_settings/xtreme_settings.h | 1 - .../PaxGod_TikTok_Marketing/frame_0.png | Bin .../PaxGod_TikTok_Marketing/frame_1.png | Bin .../PaxGod_TikTok_Marketing/frame_10.png | Bin .../PaxGod_TikTok_Marketing/frame_11.png | Bin .../PaxGod_TikTok_Marketing/frame_12.png | Bin .../PaxGod_TikTok_Marketing/frame_13.png | Bin .../PaxGod_TikTok_Marketing/frame_14.png | Bin .../PaxGod_TikTok_Marketing/frame_15.png | Bin .../PaxGod_TikTok_Marketing/frame_16.png | Bin .../PaxGod_TikTok_Marketing/frame_17.png | Bin .../PaxGod_TikTok_Marketing/frame_18.png | Bin .../PaxGod_TikTok_Marketing/frame_19.png | Bin .../PaxGod_TikTok_Marketing/frame_2.png | Bin .../PaxGod_TikTok_Marketing/frame_20.png | Bin .../PaxGod_TikTok_Marketing/frame_21.png | Bin .../PaxGod_TikTok_Marketing/frame_22.png | Bin .../PaxGod_TikTok_Marketing/frame_23.png | Bin .../PaxGod_TikTok_Marketing/frame_24.png | Bin .../PaxGod_TikTok_Marketing/frame_25.png | Bin .../PaxGod_TikTok_Marketing/frame_26.png | Bin .../PaxGod_TikTok_Marketing/frame_27.png | Bin .../PaxGod_TikTok_Marketing/frame_3.png | Bin .../PaxGod_TikTok_Marketing/frame_4.png | Bin .../PaxGod_TikTok_Marketing/frame_5.png | Bin .../PaxGod_TikTok_Marketing/frame_6.png | Bin .../PaxGod_TikTok_Marketing/frame_7.png | Bin .../PaxGod_TikTok_Marketing/frame_8.png | Bin .../PaxGod_TikTok_Marketing/frame_9.png | Bin .../Anims}/PaxGod_TikTok_Marketing/meta.txt | 0 .../NSFW/Anims}/lvl_1/frame_0.png | Bin .../NSFW/Anims}/lvl_1/frame_1.png | Bin .../NSFW/Anims}/lvl_1/frame_10.png | Bin .../NSFW/Anims}/lvl_1/frame_11.png | Bin .../NSFW/Anims}/lvl_1/frame_12.png | Bin .../NSFW/Anims}/lvl_1/frame_13.png | Bin .../NSFW/Anims}/lvl_1/frame_14.png | Bin .../NSFW/Anims}/lvl_1/frame_15.png | Bin .../NSFW/Anims}/lvl_1/frame_16.png | Bin .../NSFW/Anims}/lvl_1/frame_17.png | Bin .../NSFW/Anims}/lvl_1/frame_18.png | Bin .../NSFW/Anims}/lvl_1/frame_19.png | Bin .../NSFW/Anims}/lvl_1/frame_2.png | Bin .../NSFW/Anims}/lvl_1/frame_20.png | Bin .../NSFW/Anims}/lvl_1/frame_21.png | Bin .../NSFW/Anims}/lvl_1/frame_22.png | Bin .../NSFW/Anims}/lvl_1/frame_23.png | Bin .../NSFW/Anims}/lvl_1/frame_24.png | Bin .../NSFW/Anims}/lvl_1/frame_25.png | Bin .../NSFW/Anims}/lvl_1/frame_26.png | Bin .../NSFW/Anims}/lvl_1/frame_27.png | Bin .../NSFW/Anims}/lvl_1/frame_28.png | Bin .../NSFW/Anims}/lvl_1/frame_29.png | Bin .../NSFW/Anims}/lvl_1/frame_3.png | Bin .../NSFW/Anims}/lvl_1/frame_30.png | Bin .../NSFW/Anims}/lvl_1/frame_4.png | Bin .../NSFW/Anims}/lvl_1/frame_5.png | Bin .../NSFW/Anims}/lvl_1/frame_6.png | Bin .../NSFW/Anims}/lvl_1/frame_7.png | Bin .../NSFW/Anims}/lvl_1/frame_8.png | Bin .../NSFW/Anims}/lvl_1/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_1/meta.txt | 0 .../NSFW/Anims}/lvl_10/frame_0.png | Bin .../NSFW/Anims}/lvl_10/frame_1.png | Bin .../NSFW/Anims}/lvl_10/frame_10.png | Bin .../NSFW/Anims}/lvl_10/frame_11.png | Bin .../NSFW/Anims}/lvl_10/frame_12.png | Bin .../NSFW/Anims}/lvl_10/frame_13.png | Bin .../NSFW/Anims}/lvl_10/frame_14.png | Bin .../NSFW/Anims}/lvl_10/frame_15.png | Bin .../NSFW/Anims}/lvl_10/frame_16.png | Bin .../NSFW/Anims}/lvl_10/frame_17.png | Bin .../NSFW/Anims}/lvl_10/frame_18.png | Bin .../NSFW/Anims}/lvl_10/frame_19.png | Bin .../NSFW/Anims}/lvl_10/frame_2.png | Bin .../NSFW/Anims}/lvl_10/frame_20.png | Bin .../NSFW/Anims}/lvl_10/frame_21.png | Bin .../NSFW/Anims}/lvl_10/frame_22.png | Bin .../NSFW/Anims}/lvl_10/frame_23.png | Bin .../NSFW/Anims}/lvl_10/frame_24.png | Bin .../NSFW/Anims}/lvl_10/frame_25.png | Bin .../NSFW/Anims}/lvl_10/frame_26.png | Bin .../NSFW/Anims}/lvl_10/frame_27.png | Bin .../NSFW/Anims}/lvl_10/frame_3.png | Bin .../NSFW/Anims}/lvl_10/frame_4.png | Bin .../NSFW/Anims}/lvl_10/frame_5.png | Bin .../NSFW/Anims}/lvl_10/frame_6.png | Bin .../NSFW/Anims}/lvl_10/frame_7.png | Bin .../NSFW/Anims}/lvl_10/frame_8.png | Bin .../NSFW/Anims}/lvl_10/frame_9.png | Bin .../NSFW/Anims}/lvl_10/meta.txt | 0 .../NSFW/Anims}/lvl_11/frame_0.png | Bin .../NSFW/Anims}/lvl_11/frame_1.png | Bin .../NSFW/Anims}/lvl_11/frame_10.png | Bin .../NSFW/Anims}/lvl_11/frame_11.png | Bin .../NSFW/Anims}/lvl_11/frame_12.png | Bin .../NSFW/Anims}/lvl_11/frame_13.png | Bin .../NSFW/Anims}/lvl_11/frame_14.png | Bin .../NSFW/Anims}/lvl_11/frame_15.png | Bin .../NSFW/Anims}/lvl_11/frame_16.png | Bin .../NSFW/Anims}/lvl_11/frame_17.png | Bin .../NSFW/Anims}/lvl_11/frame_18.png | Bin .../NSFW/Anims}/lvl_11/frame_19.png | Bin .../NSFW/Anims}/lvl_11/frame_2.png | Bin .../NSFW/Anims}/lvl_11/frame_20.png | Bin .../NSFW/Anims}/lvl_11/frame_21.png | Bin .../NSFW/Anims}/lvl_11/frame_22.png | Bin .../NSFW/Anims}/lvl_11/frame_23.png | Bin .../NSFW/Anims}/lvl_11/frame_24.png | Bin .../NSFW/Anims}/lvl_11/frame_25.png | Bin .../NSFW/Anims}/lvl_11/frame_26.png | Bin .../NSFW/Anims}/lvl_11/frame_27.png | Bin .../NSFW/Anims}/lvl_11/frame_28.png | Bin .../NSFW/Anims}/lvl_11/frame_29.png | Bin .../NSFW/Anims}/lvl_11/frame_3.png | Bin .../NSFW/Anims}/lvl_11/frame_30.png | Bin .../NSFW/Anims}/lvl_11/frame_31.png | Bin .../NSFW/Anims}/lvl_11/frame_32.png | Bin .../NSFW/Anims}/lvl_11/frame_33.png | Bin .../NSFW/Anims}/lvl_11/frame_34.png | Bin .../NSFW/Anims}/lvl_11/frame_35.png | Bin .../NSFW/Anims}/lvl_11/frame_36.png | Bin .../NSFW/Anims}/lvl_11/frame_37.png | Bin .../NSFW/Anims}/lvl_11/frame_38.png | Bin .../NSFW/Anims}/lvl_11/frame_39.png | Bin .../NSFW/Anims}/lvl_11/frame_4.png | Bin .../NSFW/Anims}/lvl_11/frame_40.png | Bin .../NSFW/Anims}/lvl_11/frame_41.png | Bin .../NSFW/Anims}/lvl_11/frame_42.png | Bin .../NSFW/Anims}/lvl_11/frame_43.png | Bin .../NSFW/Anims}/lvl_11/frame_44.png | Bin .../NSFW/Anims}/lvl_11/frame_45.png | Bin .../NSFW/Anims}/lvl_11/frame_46.png | Bin .../NSFW/Anims}/lvl_11/frame_47.png | Bin .../NSFW/Anims}/lvl_11/frame_48.png | Bin .../NSFW/Anims}/lvl_11/frame_49.png | Bin .../NSFW/Anims}/lvl_11/frame_5.png | Bin .../NSFW/Anims}/lvl_11/frame_6.png | Bin .../NSFW/Anims}/lvl_11/frame_7.png | Bin .../NSFW/Anims}/lvl_11/frame_8.png | Bin .../NSFW/Anims}/lvl_11/frame_9.png | Bin .../NSFW/Anims}/lvl_11/meta.txt | 0 .../NSFW/Anims}/lvl_12/frame_0.png | Bin .../NSFW/Anims}/lvl_12/frame_1.png | Bin .../NSFW/Anims}/lvl_12/frame_10.png | Bin .../NSFW/Anims}/lvl_12/frame_11.png | Bin .../NSFW/Anims}/lvl_12/frame_12.png | Bin .../NSFW/Anims}/lvl_12/frame_13.png | Bin .../NSFW/Anims}/lvl_12/frame_14.png | Bin .../NSFW/Anims}/lvl_12/frame_15.png | Bin .../NSFW/Anims}/lvl_12/frame_2.png | Bin .../NSFW/Anims}/lvl_12/frame_3.png | Bin .../NSFW/Anims}/lvl_12/frame_4.png | Bin .../NSFW/Anims}/lvl_12/frame_5.png | Bin .../NSFW/Anims}/lvl_12/frame_6.png | Bin .../NSFW/Anims}/lvl_12/frame_7.png | Bin .../NSFW/Anims}/lvl_12/frame_8.png | Bin .../NSFW/Anims}/lvl_12/frame_9.png | Bin .../NSFW/Anims}/lvl_12/meta.txt | 0 .../NSFW/Anims}/lvl_13/frame_0.png | Bin .../NSFW/Anims}/lvl_13/frame_1.png | Bin .../NSFW/Anims}/lvl_13/frame_10.png | Bin .../NSFW/Anims}/lvl_13/frame_2.png | Bin .../NSFW/Anims}/lvl_13/frame_3.png | Bin .../NSFW/Anims}/lvl_13/frame_4.png | Bin .../NSFW/Anims}/lvl_13/frame_5.png | Bin .../NSFW/Anims}/lvl_13/frame_6.png | Bin .../NSFW/Anims}/lvl_13/frame_7.png | Bin .../NSFW/Anims}/lvl_13/frame_8.png | Bin .../NSFW/Anims}/lvl_13/frame_9.png | Bin .../NSFW/Anims}/lvl_13/meta.txt | 0 .../NSFW/Anims}/lvl_14/frame_0.png | Bin .../NSFW/Anims}/lvl_14/frame_1.png | Bin .../NSFW/Anims}/lvl_14/frame_2.png | Bin .../NSFW/Anims}/lvl_14/frame_3.png | Bin .../NSFW/Anims}/lvl_14/frame_4.png | Bin .../NSFW/Anims}/lvl_14/frame_5.png | Bin .../NSFW/Anims}/lvl_14/frame_6.png | Bin .../NSFW/Anims}/lvl_14/frame_7.png | Bin .../NSFW/Anims}/lvl_14/meta.txt | 0 .../NSFW/Anims}/lvl_15/frame_0.png | Bin .../NSFW/Anims}/lvl_15/frame_1.png | Bin .../NSFW/Anims}/lvl_15/frame_10.png | Bin .../NSFW/Anims}/lvl_15/frame_11.png | Bin .../NSFW/Anims}/lvl_15/frame_12.png | Bin .../NSFW/Anims}/lvl_15/frame_13.png | Bin .../NSFW/Anims}/lvl_15/frame_14.png | Bin .../NSFW/Anims}/lvl_15/frame_15.png | Bin .../NSFW/Anims}/lvl_15/frame_16.png | Bin .../NSFW/Anims}/lvl_15/frame_17.png | Bin .../NSFW/Anims}/lvl_15/frame_18.png | Bin .../NSFW/Anims}/lvl_15/frame_19.png | Bin .../NSFW/Anims}/lvl_15/frame_2.png | Bin .../NSFW/Anims}/lvl_15/frame_20.png | Bin .../NSFW/Anims}/lvl_15/frame_21.png | Bin .../NSFW/Anims}/lvl_15/frame_3.png | Bin .../NSFW/Anims}/lvl_15/frame_4.png | Bin .../NSFW/Anims}/lvl_15/frame_5.png | Bin .../NSFW/Anims}/lvl_15/frame_6.png | Bin .../NSFW/Anims}/lvl_15/frame_7.png | Bin .../NSFW/Anims}/lvl_15/frame_8.png | Bin .../NSFW/Anims}/lvl_15/frame_9.png | Bin .../NSFW/Anims}/lvl_15/meta.txt | 0 .../NSFW/Anims}/lvl_16/frame_0.png | Bin .../NSFW/Anims}/lvl_16/frame_1.png | Bin .../NSFW/Anims}/lvl_16/frame_10.png | Bin .../NSFW/Anims}/lvl_16/frame_11.png | Bin .../NSFW/Anims}/lvl_16/frame_12.png | Bin .../NSFW/Anims}/lvl_16/frame_13.png | Bin .../NSFW/Anims}/lvl_16/frame_14.png | Bin .../NSFW/Anims}/lvl_16/frame_15.png | Bin .../NSFW/Anims}/lvl_16/frame_16.png | Bin .../NSFW/Anims}/lvl_16/frame_17.png | Bin .../NSFW/Anims}/lvl_16/frame_18.png | Bin .../NSFW/Anims}/lvl_16/frame_19.png | Bin .../NSFW/Anims}/lvl_16/frame_2.png | Bin .../NSFW/Anims}/lvl_16/frame_20.png | Bin .../NSFW/Anims}/lvl_16/frame_3.png | Bin .../NSFW/Anims}/lvl_16/frame_4.png | Bin .../NSFW/Anims}/lvl_16/frame_5.png | Bin .../NSFW/Anims}/lvl_16/frame_6.png | Bin .../NSFW/Anims}/lvl_16/frame_7.png | Bin .../NSFW/Anims}/lvl_16/frame_8.png | Bin .../NSFW/Anims}/lvl_16/frame_9.png | Bin .../NSFW/Anims}/lvl_16/meta.txt | 0 .../NSFW/Anims}/lvl_17/frame_0.png | Bin .../NSFW/Anims}/lvl_17/frame_1.png | Bin .../NSFW/Anims}/lvl_17/frame_10.png | Bin .../NSFW/Anims}/lvl_17/frame_11.png | Bin .../NSFW/Anims}/lvl_17/frame_12.png | Bin .../NSFW/Anims}/lvl_17/frame_13.png | Bin .../NSFW/Anims}/lvl_17/frame_14.png | Bin .../NSFW/Anims}/lvl_17/frame_15.png | Bin .../NSFW/Anims}/lvl_17/frame_16.png | Bin .../NSFW/Anims}/lvl_17/frame_17.png | Bin .../NSFW/Anims}/lvl_17/frame_18.png | Bin .../NSFW/Anims}/lvl_17/frame_19.png | Bin .../NSFW/Anims}/lvl_17/frame_2.png | Bin .../NSFW/Anims}/lvl_17/frame_20.png | Bin .../NSFW/Anims}/lvl_17/frame_21.png | Bin .../NSFW/Anims}/lvl_17/frame_22.png | Bin .../NSFW/Anims}/lvl_17/frame_23.png | Bin .../NSFW/Anims}/lvl_17/frame_24.png | Bin .../NSFW/Anims}/lvl_17/frame_25.png | Bin .../NSFW/Anims}/lvl_17/frame_26.png | Bin .../NSFW/Anims}/lvl_17/frame_27.png | Bin .../NSFW/Anims}/lvl_17/frame_28.png | Bin .../NSFW/Anims}/lvl_17/frame_29.png | Bin .../NSFW/Anims}/lvl_17/frame_3.png | Bin .../NSFW/Anims}/lvl_17/frame_30.png | Bin .../NSFW/Anims}/lvl_17/frame_31.png | Bin .../NSFW/Anims}/lvl_17/frame_4.png | Bin .../NSFW/Anims}/lvl_17/frame_5.png | Bin .../NSFW/Anims}/lvl_17/frame_6.png | Bin .../NSFW/Anims}/lvl_17/frame_7.png | Bin .../NSFW/Anims}/lvl_17/frame_8.png | Bin .../NSFW/Anims}/lvl_17/frame_9.png | Bin .../NSFW/Anims}/lvl_17/meta.txt | 0 .../NSFW/Anims}/lvl_18/frame_0.png | Bin .../NSFW/Anims}/lvl_18/frame_1.png | Bin .../NSFW/Anims}/lvl_18/frame_10.png | Bin .../NSFW/Anims}/lvl_18/frame_11.png | Bin .../NSFW/Anims}/lvl_18/frame_12.png | Bin .../NSFW/Anims}/lvl_18/frame_13.png | Bin .../NSFW/Anims}/lvl_18/frame_14.png | Bin .../NSFW/Anims}/lvl_18/frame_15.png | Bin .../NSFW/Anims}/lvl_18/frame_16.png | Bin .../NSFW/Anims}/lvl_18/frame_17.png | Bin .../NSFW/Anims}/lvl_18/frame_18.png | Bin .../NSFW/Anims}/lvl_18/frame_19.png | Bin .../NSFW/Anims}/lvl_18/frame_2.png | Bin .../NSFW/Anims}/lvl_18/frame_20.png | Bin .../NSFW/Anims}/lvl_18/frame_21.png | Bin .../NSFW/Anims}/lvl_18/frame_22.png | Bin .../NSFW/Anims}/lvl_18/frame_3.png | Bin .../NSFW/Anims}/lvl_18/frame_4.png | Bin .../NSFW/Anims}/lvl_18/frame_5.png | Bin .../NSFW/Anims}/lvl_18/frame_6.png | Bin .../NSFW/Anims}/lvl_18/frame_7.png | Bin .../NSFW/Anims}/lvl_18/frame_8.png | Bin .../NSFW/Anims}/lvl_18/frame_9.png | Bin .../NSFW/Anims}/lvl_18/meta.txt | 0 .../NSFW/Anims}/lvl_19/frame_0.png | Bin .../NSFW/Anims}/lvl_19/frame_1.png | Bin .../NSFW/Anims}/lvl_19/frame_10.png | Bin .../NSFW/Anims}/lvl_19/frame_11.png | Bin .../NSFW/Anims}/lvl_19/frame_12.png | Bin .../NSFW/Anims}/lvl_19/frame_13.png | Bin .../NSFW/Anims}/lvl_19/frame_14.png | Bin .../NSFW/Anims}/lvl_19/frame_15.png | Bin .../NSFW/Anims}/lvl_19/frame_16.png | Bin .../NSFW/Anims}/lvl_19/frame_17.png | Bin .../NSFW/Anims}/lvl_19/frame_18.png | Bin .../NSFW/Anims}/lvl_19/frame_19.png | Bin .../NSFW/Anims}/lvl_19/frame_2.png | Bin .../NSFW/Anims}/lvl_19/frame_20.png | Bin .../NSFW/Anims}/lvl_19/frame_21.png | Bin .../NSFW/Anims}/lvl_19/frame_3.png | Bin .../NSFW/Anims}/lvl_19/frame_4.png | Bin .../NSFW/Anims}/lvl_19/frame_5.png | Bin .../NSFW/Anims}/lvl_19/frame_6.png | Bin .../NSFW/Anims}/lvl_19/frame_7.png | Bin .../NSFW/Anims}/lvl_19/frame_8.png | Bin .../NSFW/Anims}/lvl_19/frame_9.png | Bin .../NSFW/Anims}/lvl_19/meta.txt | 0 .../NSFW/Anims}/lvl_2/frame_0.png | Bin .../NSFW/Anims}/lvl_2/frame_1.png | Bin .../NSFW/Anims}/lvl_2/frame_10.png | Bin .../NSFW/Anims}/lvl_2/frame_11.png | Bin .../NSFW/Anims}/lvl_2/frame_12.png | Bin .../NSFW/Anims}/lvl_2/frame_13.png | Bin .../NSFW/Anims}/lvl_2/frame_14.png | Bin .../NSFW/Anims}/lvl_2/frame_2.png | Bin .../NSFW/Anims}/lvl_2/frame_3.png | Bin .../NSFW/Anims}/lvl_2/frame_4.png | Bin .../NSFW/Anims}/lvl_2/frame_5.png | Bin .../NSFW/Anims}/lvl_2/frame_6.png | Bin .../NSFW/Anims}/lvl_2/frame_7.png | Bin .../NSFW/Anims}/lvl_2/frame_8.png | Bin .../NSFW/Anims}/lvl_2/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_2/meta.txt | 0 .../NSFW/Anims}/lvl_20/frame_0.png | Bin .../NSFW/Anims}/lvl_20/frame_1.png | Bin .../NSFW/Anims}/lvl_20/frame_10.png | Bin .../NSFW/Anims}/lvl_20/frame_11.png | Bin .../NSFW/Anims}/lvl_20/frame_12.png | Bin .../NSFW/Anims}/lvl_20/frame_13.png | Bin .../NSFW/Anims}/lvl_20/frame_2.png | Bin .../NSFW/Anims}/lvl_20/frame_3.png | Bin .../NSFW/Anims}/lvl_20/frame_4.png | Bin .../NSFW/Anims}/lvl_20/frame_5.png | Bin .../NSFW/Anims}/lvl_20/frame_6.png | Bin .../NSFW/Anims}/lvl_20/frame_7.png | Bin .../NSFW/Anims}/lvl_20/frame_8.png | Bin .../NSFW/Anims}/lvl_20/frame_9.png | Bin .../NSFW/Anims}/lvl_20/meta.txt | 0 .../NSFW/Anims}/lvl_21/frame_0.png | Bin .../NSFW/Anims}/lvl_21/frame_1.png | Bin .../NSFW/Anims}/lvl_21/frame_2.png | Bin .../NSFW/Anims}/lvl_21/frame_3.png | Bin .../NSFW/Anims}/lvl_21/frame_4.png | Bin .../NSFW/Anims}/lvl_21/frame_5.png | Bin .../NSFW/Anims}/lvl_21/meta.txt | 0 .../NSFW/Anims}/lvl_22/frame_0.png | Bin .../NSFW/Anims}/lvl_22/frame_1.png | Bin .../NSFW/Anims}/lvl_22/frame_10.png | Bin .../NSFW/Anims}/lvl_22/frame_11.png | Bin .../NSFW/Anims}/lvl_22/frame_12.png | Bin .../NSFW/Anims}/lvl_22/frame_13.png | Bin .../NSFW/Anims}/lvl_22/frame_14.png | Bin .../NSFW/Anims}/lvl_22/frame_15.png | Bin .../NSFW/Anims}/lvl_22/frame_16.png | Bin .../NSFW/Anims}/lvl_22/frame_17.png | Bin .../NSFW/Anims}/lvl_22/frame_18.png | Bin .../NSFW/Anims}/lvl_22/frame_19.png | Bin .../NSFW/Anims}/lvl_22/frame_2.png | Bin .../NSFW/Anims}/lvl_22/frame_20.png | Bin .../NSFW/Anims}/lvl_22/frame_21.png | Bin .../NSFW/Anims}/lvl_22/frame_22.png | Bin .../NSFW/Anims}/lvl_22/frame_23.png | Bin .../NSFW/Anims}/lvl_22/frame_24.png | Bin .../NSFW/Anims}/lvl_22/frame_25.png | Bin .../NSFW/Anims}/lvl_22/frame_26.png | Bin .../NSFW/Anims}/lvl_22/frame_27.png | Bin .../NSFW/Anims}/lvl_22/frame_28.png | Bin .../NSFW/Anims}/lvl_22/frame_29.png | Bin .../NSFW/Anims}/lvl_22/frame_3.png | Bin .../NSFW/Anims}/lvl_22/frame_30.png | Bin .../NSFW/Anims}/lvl_22/frame_31.png | Bin .../NSFW/Anims}/lvl_22/frame_32.png | Bin .../NSFW/Anims}/lvl_22/frame_33.png | Bin .../NSFW/Anims}/lvl_22/frame_34.png | Bin .../NSFW/Anims}/lvl_22/frame_35.png | Bin .../NSFW/Anims}/lvl_22/frame_36.png | Bin .../NSFW/Anims}/lvl_22/frame_37.png | Bin .../NSFW/Anims}/lvl_22/frame_38.png | Bin .../NSFW/Anims}/lvl_22/frame_39.png | Bin .../NSFW/Anims}/lvl_22/frame_4.png | Bin .../NSFW/Anims}/lvl_22/frame_40.png | Bin .../NSFW/Anims}/lvl_22/frame_41.png | Bin .../NSFW/Anims}/lvl_22/frame_42.png | Bin .../NSFW/Anims}/lvl_22/frame_43.png | Bin .../NSFW/Anims}/lvl_22/frame_44.png | Bin .../NSFW/Anims}/lvl_22/frame_45.png | Bin .../NSFW/Anims}/lvl_22/frame_46.png | Bin .../NSFW/Anims}/lvl_22/frame_47.png | Bin .../NSFW/Anims}/lvl_22/frame_48.png | Bin .../NSFW/Anims}/lvl_22/frame_49.png | Bin .../NSFW/Anims}/lvl_22/frame_5.png | Bin .../NSFW/Anims}/lvl_22/frame_50.png | Bin .../NSFW/Anims}/lvl_22/frame_51.png | Bin .../NSFW/Anims}/lvl_22/frame_52.png | Bin .../NSFW/Anims}/lvl_22/frame_53.png | Bin .../NSFW/Anims}/lvl_22/frame_54.png | Bin .../NSFW/Anims}/lvl_22/frame_55.png | Bin .../NSFW/Anims}/lvl_22/frame_56.png | Bin .../NSFW/Anims}/lvl_22/frame_57.png | Bin .../NSFW/Anims}/lvl_22/frame_58.png | Bin .../NSFW/Anims}/lvl_22/frame_59.png | Bin .../NSFW/Anims}/lvl_22/frame_6.png | Bin .../NSFW/Anims}/lvl_22/frame_7.png | Bin .../NSFW/Anims}/lvl_22/frame_8.png | Bin .../NSFW/Anims}/lvl_22/frame_9.png | Bin .../NSFW/Anims}/lvl_22/meta.txt | 0 .../NSFW/Anims}/lvl_23/frame_0.png | Bin .../NSFW/Anims}/lvl_23/frame_1.png | Bin .../NSFW/Anims}/lvl_23/frame_10.png | Bin .../NSFW/Anims}/lvl_23/frame_11.png | Bin .../NSFW/Anims}/lvl_23/frame_12.png | Bin .../NSFW/Anims}/lvl_23/frame_13.png | Bin .../NSFW/Anims}/lvl_23/frame_14.png | Bin .../NSFW/Anims}/lvl_23/frame_15.png | Bin .../NSFW/Anims}/lvl_23/frame_16.png | Bin .../NSFW/Anims}/lvl_23/frame_2.png | Bin .../NSFW/Anims}/lvl_23/frame_3.png | Bin .../NSFW/Anims}/lvl_23/frame_4.png | Bin .../NSFW/Anims}/lvl_23/frame_5.png | Bin .../NSFW/Anims}/lvl_23/frame_6.png | Bin .../NSFW/Anims}/lvl_23/frame_7.png | Bin .../NSFW/Anims}/lvl_23/frame_8.png | Bin .../NSFW/Anims}/lvl_23/frame_9.png | Bin .../NSFW/Anims}/lvl_23/meta.txt | 0 .../NSFW/Anims}/lvl_24/frame_0.png | Bin .../NSFW/Anims}/lvl_24/frame_1.png | Bin .../NSFW/Anims}/lvl_24/frame_10.png | Bin .../NSFW/Anims}/lvl_24/frame_11.png | Bin .../NSFW/Anims}/lvl_24/frame_12.png | Bin .../NSFW/Anims}/lvl_24/frame_13.png | Bin .../NSFW/Anims}/lvl_24/frame_14.png | Bin .../NSFW/Anims}/lvl_24/frame_15.png | Bin .../NSFW/Anims}/lvl_24/frame_16.png | Bin .../NSFW/Anims}/lvl_24/frame_17.png | Bin .../NSFW/Anims}/lvl_24/frame_18.png | Bin .../NSFW/Anims}/lvl_24/frame_19.png | Bin .../NSFW/Anims}/lvl_24/frame_2.png | Bin .../NSFW/Anims}/lvl_24/frame_20.png | Bin .../NSFW/Anims}/lvl_24/frame_21.png | Bin .../NSFW/Anims}/lvl_24/frame_22.png | Bin .../NSFW/Anims}/lvl_24/frame_23.png | Bin .../NSFW/Anims}/lvl_24/frame_24.png | Bin .../NSFW/Anims}/lvl_24/frame_25.png | Bin .../NSFW/Anims}/lvl_24/frame_26.png | Bin .../NSFW/Anims}/lvl_24/frame_27.png | Bin .../NSFW/Anims}/lvl_24/frame_28.png | Bin .../NSFW/Anims}/lvl_24/frame_29.png | Bin .../NSFW/Anims}/lvl_24/frame_3.png | Bin .../NSFW/Anims}/lvl_24/frame_4.png | Bin .../NSFW/Anims}/lvl_24/frame_5.png | Bin .../NSFW/Anims}/lvl_24/frame_6.png | Bin .../NSFW/Anims}/lvl_24/frame_7.png | Bin .../NSFW/Anims}/lvl_24/frame_8.png | Bin .../NSFW/Anims}/lvl_24/frame_9.png | Bin .../NSFW/Anims}/lvl_24/meta.txt | 0 .../NSFW/Anims}/lvl_25/frame_0.png | Bin .../NSFW/Anims}/lvl_25/frame_1.png | Bin .../NSFW/Anims}/lvl_25/frame_10.png | Bin .../NSFW/Anims}/lvl_25/frame_11.png | Bin .../NSFW/Anims}/lvl_25/frame_12.png | Bin .../NSFW/Anims}/lvl_25/frame_13.png | Bin .../NSFW/Anims}/lvl_25/frame_14.png | Bin .../NSFW/Anims}/lvl_25/frame_15.png | Bin .../NSFW/Anims}/lvl_25/frame_16.png | Bin .../NSFW/Anims}/lvl_25/frame_17.png | Bin .../NSFW/Anims}/lvl_25/frame_18.png | Bin .../NSFW/Anims}/lvl_25/frame_19.png | Bin .../NSFW/Anims}/lvl_25/frame_2.png | Bin .../NSFW/Anims}/lvl_25/frame_20.png | Bin .../NSFW/Anims}/lvl_25/frame_21.png | Bin .../NSFW/Anims}/lvl_25/frame_22.png | Bin .../NSFW/Anims}/lvl_25/frame_23.png | Bin .../NSFW/Anims}/lvl_25/frame_24.png | Bin .../NSFW/Anims}/lvl_25/frame_25.png | Bin .../NSFW/Anims}/lvl_25/frame_26.png | Bin .../NSFW/Anims}/lvl_25/frame_27.png | Bin .../NSFW/Anims}/lvl_25/frame_28.png | Bin .../NSFW/Anims}/lvl_25/frame_29.png | Bin .../NSFW/Anims}/lvl_25/frame_3.png | Bin .../NSFW/Anims}/lvl_25/frame_30.png | Bin .../NSFW/Anims}/lvl_25/frame_31.png | Bin .../NSFW/Anims}/lvl_25/frame_32.png | Bin .../NSFW/Anims}/lvl_25/frame_33.png | Bin .../NSFW/Anims}/lvl_25/frame_34.png | Bin .../NSFW/Anims}/lvl_25/frame_35.png | Bin .../NSFW/Anims}/lvl_25/frame_4.png | Bin .../NSFW/Anims}/lvl_25/frame_5.png | Bin .../NSFW/Anims}/lvl_25/frame_6.png | Bin .../NSFW/Anims}/lvl_25/frame_7.png | Bin .../NSFW/Anims}/lvl_25/frame_8.png | Bin .../NSFW/Anims}/lvl_25/frame_9.png | Bin .../NSFW/Anims}/lvl_25/meta.txt | 0 .../NSFW/Anims}/lvl_26/frame_0.png | Bin .../NSFW/Anims}/lvl_26/frame_1.png | Bin .../NSFW/Anims}/lvl_26/frame_10.png | Bin .../NSFW/Anims}/lvl_26/frame_11.png | Bin .../NSFW/Anims}/lvl_26/frame_2.png | Bin .../NSFW/Anims}/lvl_26/frame_3.png | Bin .../NSFW/Anims}/lvl_26/frame_4.png | Bin .../NSFW/Anims}/lvl_26/frame_5.png | Bin .../NSFW/Anims}/lvl_26/frame_6.png | Bin .../NSFW/Anims}/lvl_26/frame_7.png | Bin .../NSFW/Anims}/lvl_26/frame_8.png | Bin .../NSFW/Anims}/lvl_26/frame_9.png | Bin .../NSFW/Anims}/lvl_26/meta.txt | 0 .../NSFW/Anims}/lvl_27/frame_0.png | Bin .../NSFW/Anims}/lvl_27/frame_1.png | Bin .../NSFW/Anims}/lvl_27/frame_10.png | Bin .../NSFW/Anims}/lvl_27/frame_11.png | Bin .../NSFW/Anims}/lvl_27/frame_12.png | Bin .../NSFW/Anims}/lvl_27/frame_13.png | Bin .../NSFW/Anims}/lvl_27/frame_14.png | Bin .../NSFW/Anims}/lvl_27/frame_15.png | Bin .../NSFW/Anims}/lvl_27/frame_16.png | Bin .../NSFW/Anims}/lvl_27/frame_17.png | Bin .../NSFW/Anims}/lvl_27/frame_18.png | Bin .../NSFW/Anims}/lvl_27/frame_19.png | Bin .../NSFW/Anims}/lvl_27/frame_2.png | Bin .../NSFW/Anims}/lvl_27/frame_20.png | Bin .../NSFW/Anims}/lvl_27/frame_21.png | Bin .../NSFW/Anims}/lvl_27/frame_3.png | Bin .../NSFW/Anims}/lvl_27/frame_4.png | Bin .../NSFW/Anims}/lvl_27/frame_5.png | Bin .../NSFW/Anims}/lvl_27/frame_6.png | Bin .../NSFW/Anims}/lvl_27/frame_7.png | Bin .../NSFW/Anims}/lvl_27/frame_8.png | Bin .../NSFW/Anims}/lvl_27/frame_9.png | Bin .../NSFW/Anims}/lvl_27/meta.txt | 0 .../NSFW/Anims}/lvl_28/frame_0.png | Bin .../NSFW/Anims}/lvl_28/frame_1.png | Bin .../NSFW/Anims}/lvl_28/frame_2.png | Bin .../NSFW/Anims}/lvl_28/frame_3.png | Bin .../NSFW/Anims}/lvl_28/frame_4.png | Bin .../NSFW/Anims}/lvl_28/frame_5.png | Bin .../NSFW/Anims}/lvl_28/meta.txt | 0 .../NSFW/Anims}/lvl_29/frame_0.png | Bin .../NSFW/Anims}/lvl_29/frame_1.png | Bin .../NSFW/Anims}/lvl_29/frame_10.png | Bin .../NSFW/Anims}/lvl_29/frame_11.png | Bin .../NSFW/Anims}/lvl_29/frame_12.png | Bin .../NSFW/Anims}/lvl_29/frame_13.png | Bin .../NSFW/Anims}/lvl_29/frame_14.png | Bin .../NSFW/Anims}/lvl_29/frame_15.png | Bin .../NSFW/Anims}/lvl_29/frame_16.png | Bin .../NSFW/Anims}/lvl_29/frame_17.png | Bin .../NSFW/Anims}/lvl_29/frame_18.png | Bin .../NSFW/Anims}/lvl_29/frame_19.png | Bin .../NSFW/Anims}/lvl_29/frame_2.png | Bin .../NSFW/Anims}/lvl_29/frame_20.png | Bin .../NSFW/Anims}/lvl_29/frame_21.png | Bin .../NSFW/Anims}/lvl_29/frame_22.png | Bin .../NSFW/Anims}/lvl_29/frame_23.png | Bin .../NSFW/Anims}/lvl_29/frame_24.png | Bin .../NSFW/Anims}/lvl_29/frame_25.png | Bin .../NSFW/Anims}/lvl_29/frame_26.png | Bin .../NSFW/Anims}/lvl_29/frame_27.png | Bin .../NSFW/Anims}/lvl_29/frame_28.png | Bin .../NSFW/Anims}/lvl_29/frame_29.png | Bin .../NSFW/Anims}/lvl_29/frame_3.png | Bin .../NSFW/Anims}/lvl_29/frame_30.png | Bin .../NSFW/Anims}/lvl_29/frame_31.png | Bin .../NSFW/Anims}/lvl_29/frame_32.png | Bin .../NSFW/Anims}/lvl_29/frame_33.png | Bin .../NSFW/Anims}/lvl_29/frame_34.png | Bin .../NSFW/Anims}/lvl_29/frame_35.png | Bin .../NSFW/Anims}/lvl_29/frame_36.png | Bin .../NSFW/Anims}/lvl_29/frame_37.png | Bin .../NSFW/Anims}/lvl_29/frame_38.png | Bin .../NSFW/Anims}/lvl_29/frame_39.png | Bin .../NSFW/Anims}/lvl_29/frame_4.png | Bin .../NSFW/Anims}/lvl_29/frame_40.png | Bin .../NSFW/Anims}/lvl_29/frame_41.png | Bin .../NSFW/Anims}/lvl_29/frame_42.png | Bin .../NSFW/Anims}/lvl_29/frame_43.png | Bin .../NSFW/Anims}/lvl_29/frame_44.png | Bin .../NSFW/Anims}/lvl_29/frame_45.png | Bin .../NSFW/Anims}/lvl_29/frame_46.png | Bin .../NSFW/Anims}/lvl_29/frame_47.png | Bin .../NSFW/Anims}/lvl_29/frame_48.png | Bin .../NSFW/Anims}/lvl_29/frame_49.png | Bin .../NSFW/Anims}/lvl_29/frame_5.png | Bin .../NSFW/Anims}/lvl_29/frame_50.png | Bin .../NSFW/Anims}/lvl_29/frame_51.png | Bin .../NSFW/Anims}/lvl_29/frame_6.png | Bin .../NSFW/Anims}/lvl_29/frame_7.png | Bin .../NSFW/Anims}/lvl_29/frame_8.png | Bin .../NSFW/Anims}/lvl_29/frame_9.png | Bin .../NSFW/Anims}/lvl_29/meta.txt | 0 .../NSFW/Anims}/lvl_3/frame_0.png | Bin .../NSFW/Anims}/lvl_3/frame_1.png | Bin .../NSFW/Anims}/lvl_3/frame_10.png | Bin .../NSFW/Anims}/lvl_3/frame_11.png | Bin .../NSFW/Anims}/lvl_3/frame_12.png | Bin .../NSFW/Anims}/lvl_3/frame_13.png | Bin .../NSFW/Anims}/lvl_3/frame_14.png | Bin .../NSFW/Anims}/lvl_3/frame_2.png | Bin .../NSFW/Anims}/lvl_3/frame_3.png | Bin .../NSFW/Anims}/lvl_3/frame_4.png | Bin .../NSFW/Anims}/lvl_3/frame_5.png | Bin .../NSFW/Anims}/lvl_3/frame_6.png | Bin .../NSFW/Anims}/lvl_3/frame_7.png | Bin .../NSFW/Anims}/lvl_3/frame_8.png | Bin .../NSFW/Anims}/lvl_3/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_3/meta.txt | 0 .../NSFW/Anims}/lvl_30/frame_0.png | Bin .../NSFW/Anims}/lvl_30/frame_1.png | Bin .../NSFW/Anims}/lvl_30/frame_10.png | Bin .../NSFW/Anims}/lvl_30/frame_11.png | Bin .../NSFW/Anims}/lvl_30/frame_12.png | Bin .../NSFW/Anims}/lvl_30/frame_13.png | Bin .../NSFW/Anims}/lvl_30/frame_14.png | Bin .../NSFW/Anims}/lvl_30/frame_15.png | Bin .../NSFW/Anims}/lvl_30/frame_16.png | Bin .../NSFW/Anims}/lvl_30/frame_17.png | Bin .../NSFW/Anims}/lvl_30/frame_18.png | Bin .../NSFW/Anims}/lvl_30/frame_19.png | Bin .../NSFW/Anims}/lvl_30/frame_2.png | Bin .../NSFW/Anims}/lvl_30/frame_20.png | Bin .../NSFW/Anims}/lvl_30/frame_21.png | Bin .../NSFW/Anims}/lvl_30/frame_22.png | Bin .../NSFW/Anims}/lvl_30/frame_23.png | Bin .../NSFW/Anims}/lvl_30/frame_24.png | Bin .../NSFW/Anims}/lvl_30/frame_25.png | Bin .../NSFW/Anims}/lvl_30/frame_26.png | Bin .../NSFW/Anims}/lvl_30/frame_27.png | Bin .../NSFW/Anims}/lvl_30/frame_28.png | Bin .../NSFW/Anims}/lvl_30/frame_29.png | Bin .../NSFW/Anims}/lvl_30/frame_3.png | Bin .../NSFW/Anims}/lvl_30/frame_30.png | Bin .../NSFW/Anims}/lvl_30/frame_31.png | Bin .../NSFW/Anims}/lvl_30/frame_32.png | Bin .../NSFW/Anims}/lvl_30/frame_33.png | Bin .../NSFW/Anims}/lvl_30/frame_34.png | Bin .../NSFW/Anims}/lvl_30/frame_35.png | Bin .../NSFW/Anims}/lvl_30/frame_36.png | Bin .../NSFW/Anims}/lvl_30/frame_37.png | Bin .../NSFW/Anims}/lvl_30/frame_38.png | Bin .../NSFW/Anims}/lvl_30/frame_39.png | Bin .../NSFW/Anims}/lvl_30/frame_4.png | Bin .../NSFW/Anims}/lvl_30/frame_40.png | Bin .../NSFW/Anims}/lvl_30/frame_41.png | Bin .../NSFW/Anims}/lvl_30/frame_42.png | Bin .../NSFW/Anims}/lvl_30/frame_43.png | Bin .../NSFW/Anims}/lvl_30/frame_44.png | Bin .../NSFW/Anims}/lvl_30/frame_45.png | Bin .../NSFW/Anims}/lvl_30/frame_46.png | Bin .../NSFW/Anims}/lvl_30/frame_47.png | Bin .../NSFW/Anims}/lvl_30/frame_48.png | Bin .../NSFW/Anims}/lvl_30/frame_49.png | Bin .../NSFW/Anims}/lvl_30/frame_5.png | Bin .../NSFW/Anims}/lvl_30/frame_6.png | Bin .../NSFW/Anims}/lvl_30/frame_7.png | Bin .../NSFW/Anims}/lvl_30/frame_8.png | Bin .../NSFW/Anims}/lvl_30/frame_9.png | Bin .../NSFW/Anims}/lvl_30/meta.txt | 0 .../NSFW/Anims}/lvl_4/frame_0.png | Bin .../NSFW/Anims}/lvl_4/frame_1.png | Bin .../NSFW/Anims}/lvl_4/frame_10.png | Bin .../NSFW/Anims}/lvl_4/frame_11.png | Bin .../NSFW/Anims}/lvl_4/frame_12.png | Bin .../NSFW/Anims}/lvl_4/frame_13.png | Bin .../NSFW/Anims}/lvl_4/frame_14.png | Bin .../NSFW/Anims}/lvl_4/frame_15.png | Bin .../NSFW/Anims}/lvl_4/frame_16.png | Bin .../NSFW/Anims}/lvl_4/frame_17.png | Bin .../NSFW/Anims}/lvl_4/frame_18.png | Bin .../NSFW/Anims}/lvl_4/frame_19.png | Bin .../NSFW/Anims}/lvl_4/frame_2.png | Bin .../NSFW/Anims}/lvl_4/frame_3.png | Bin .../NSFW/Anims}/lvl_4/frame_4.png | Bin .../NSFW/Anims}/lvl_4/frame_5.png | Bin .../NSFW/Anims}/lvl_4/frame_6.png | Bin .../NSFW/Anims}/lvl_4/frame_7.png | Bin .../NSFW/Anims}/lvl_4/frame_8.png | Bin .../NSFW/Anims}/lvl_4/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_4/meta.txt | 0 .../NSFW/Anims}/lvl_5/frame_0.png | Bin .../NSFW/Anims}/lvl_5/frame_1.png | Bin .../NSFW/Anims}/lvl_5/frame_10.png | Bin .../NSFW/Anims}/lvl_5/frame_11.png | Bin .../NSFW/Anims}/lvl_5/frame_12.png | Bin .../NSFW/Anims}/lvl_5/frame_13.png | Bin .../NSFW/Anims}/lvl_5/frame_14.png | Bin .../NSFW/Anims}/lvl_5/frame_15.png | Bin .../NSFW/Anims}/lvl_5/frame_16.png | Bin .../NSFW/Anims}/lvl_5/frame_17.png | Bin .../NSFW/Anims}/lvl_5/frame_18.png | Bin .../NSFW/Anims}/lvl_5/frame_19.png | Bin .../NSFW/Anims}/lvl_5/frame_2.png | Bin .../NSFW/Anims}/lvl_5/frame_20.png | Bin .../NSFW/Anims}/lvl_5/frame_21.png | Bin .../NSFW/Anims}/lvl_5/frame_22.png | Bin .../NSFW/Anims}/lvl_5/frame_23.png | Bin .../NSFW/Anims}/lvl_5/frame_24.png | Bin .../NSFW/Anims}/lvl_5/frame_25.png | Bin .../NSFW/Anims}/lvl_5/frame_26.png | Bin .../NSFW/Anims}/lvl_5/frame_27.png | Bin .../NSFW/Anims}/lvl_5/frame_3.png | Bin .../NSFW/Anims}/lvl_5/frame_4.png | Bin .../NSFW/Anims}/lvl_5/frame_5.png | Bin .../NSFW/Anims}/lvl_5/frame_6.png | Bin .../NSFW/Anims}/lvl_5/frame_7.png | Bin .../NSFW/Anims}/lvl_5/frame_8.png | Bin .../NSFW/Anims}/lvl_5/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_5/meta.txt | 0 .../NSFW/Anims}/lvl_6/frame_0.png | Bin .../NSFW/Anims}/lvl_6/frame_1.png | Bin .../NSFW/Anims}/lvl_6/frame_2.png | Bin .../NSFW/Anims}/lvl_6/frame_3.png | Bin .../NSFW/Anims}/lvl_6/frame_4.png | Bin .../NSFW/Anims}/lvl_6/frame_5.png | Bin .../NSFW/Anims}/lvl_6/frame_6.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_6/meta.txt | 0 .../NSFW/Anims}/lvl_7/frame_0.png | Bin .../NSFW/Anims}/lvl_7/frame_1.png | Bin .../NSFW/Anims}/lvl_7/frame_10.png | Bin .../NSFW/Anims}/lvl_7/frame_11.png | Bin .../NSFW/Anims}/lvl_7/frame_12.png | Bin .../NSFW/Anims}/lvl_7/frame_13.png | Bin .../NSFW/Anims}/lvl_7/frame_2.png | Bin .../NSFW/Anims}/lvl_7/frame_3.png | Bin .../NSFW/Anims}/lvl_7/frame_4.png | Bin .../NSFW/Anims}/lvl_7/frame_5.png | Bin .../NSFW/Anims}/lvl_7/frame_6.png | Bin .../NSFW/Anims}/lvl_7/frame_7.png | Bin .../NSFW/Anims}/lvl_7/frame_8.png | Bin .../NSFW/Anims}/lvl_7/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_7/meta.txt | 0 .../NSFW/Anims}/lvl_8/frame_0.png | Bin .../NSFW/Anims}/lvl_8/frame_1.png | Bin .../NSFW/Anims}/lvl_8/frame_2.png | Bin .../NSFW/Anims}/lvl_8/frame_3.png | Bin .../NSFW/Anims}/lvl_8/frame_4.png | Bin .../NSFW/Anims}/lvl_8/frame_5.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_8/meta.txt | 0 .../NSFW/Anims}/lvl_9/frame_0.png | Bin .../NSFW/Anims}/lvl_9/frame_1.png | Bin .../NSFW/Anims}/lvl_9/frame_2.png | Bin .../NSFW/Anims}/lvl_9/frame_3.png | Bin .../NSFW/Anims}/lvl_9/frame_4.png | Bin .../NSFW/Anims}/lvl_9/frame_5.png | Bin .../NSFW/Anims}/lvl_9/frame_6.png | Bin .../NSFW/Anims}/lvl_9/frame_7.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_9/meta.txt | 0 .../nsfw => custom/NSFW/Anims}/manifest.txt | 0 .../Animations/Levelup_128x64}/frame_00.png | Bin .../Animations/Levelup_128x64}/frame_01.png | Bin .../Animations/Levelup_128x64}/frame_02.png | Bin .../Animations/Levelup_128x64}/frame_03.png | Bin .../Animations/Levelup_128x64}/frame_04.png | Bin .../Animations/Levelup_128x64}/frame_05.png | Bin .../Animations/Levelup_128x64}/frame_06.png | Bin .../Animations/Levelup_128x64}/frame_07.png | Bin .../Animations/Levelup_128x64}/frame_08.png | Bin .../Animations/Levelup_128x64}/frame_09.png | Bin .../Animations/Levelup_128x64}/frame_10.png | Bin .../Animations/Levelup_128x64}/frame_11.png | Bin .../Animations/Levelup_128x64}/frame_12.png | Bin .../Animations/Levelup_128x64}/frame_13.png | Bin .../Animations/Levelup_128x64}/frame_14.png | Bin .../Animations/Levelup_128x64}/frame_15.png | Bin .../Animations/Levelup_128x64}/frame_16.png | Bin .../Animations/Levelup_128x64}/frame_17.png | Bin .../Animations/Levelup_128x64}/frame_18.png | Bin .../Animations/Levelup_128x64}/frame_19.png | Bin .../Animations/Levelup_128x64}/frame_20.png | Bin .../Animations/Levelup_128x64}/frame_21.png | Bin .../Animations/Levelup_128x64}/frame_22.png | Bin .../Animations/Levelup_128x64}/frame_23.png | Bin .../Animations/Levelup_128x64}/frame_24.png | Bin .../Animations/Levelup_128x64}/frame_25.png | Bin .../Animations/Levelup_128x64}/frame_26.png | Bin .../Animations/Levelup_128x64}/frame_27.png | Bin .../Animations/Levelup_128x64}/frame_28.png | Bin .../Animations/Levelup_128x64}/frame_29.png | Bin .../Animations/Levelup_128x64}/frame_30.png | Bin .../Animations/Levelup_128x64}/frame_31.png | Bin .../Animations/Levelup_128x64}/frame_rate | 0 .../NSFW/Icons/BLE/BLE_Pairing_128x64.png | Bin 0 -> 2610 bytes .../Icons/Dolphin/DolphinCommon_56x48.png | Bin 0 -> 3376 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 0 -> 5390 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 0 -> 4224 bytes .../NSFW/Icons/Passport/passport_DB.png | Bin 0 -> 852 bytes .../Icons/Passport/passport_bad_46x49.png} | Bin .../Icons/Passport/passport_happy_46x49.png | Bin 0 -> 6373 bytes .../Icons/Passport/passport_okay_46x49.png | Bin 0 -> 6373 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.png | Bin 0 -> 4862 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.png | Bin 0 -> 4882 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 0 -> 4466 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.png | Bin 0 -> 3798 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.png | Bin 0 -> 4092 bytes .../custom/NSFW/Icons/U2F/Auth_62x31.png | Bin 0 -> 1864 bytes .../NSFW/Icons/U2F/Connect_me_62x31.png | Bin 0 -> 1895 bytes .../custom/NSFW/Icons/U2F/Connected_62x31.png | Bin 0 -> 1874 bytes .../custom/NSFW/Icons/U2F/Error_62x31.png | Bin 0 -> 1863 bytes .../Icons/iButton/DolphinMafia_115x62.png | Bin 0 -> 6876 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.png | Bin 0 -> 5422 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.png | Bin 0 -> 5122 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 0 -> 4719 bytes .../{sfw => }/L1_Boxing_128x64/frame_0.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_1.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_2.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_3.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_4.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_5.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_6.png | Bin .../{sfw => }/L1_Boxing_128x64/meta.txt | 0 .../{sfw => }/L1_Cry_128x64/frame_0.png | Bin .../{sfw => }/L1_Cry_128x64/frame_1.png | Bin .../{sfw => }/L1_Cry_128x64/frame_2.png | Bin .../{sfw => }/L1_Cry_128x64/frame_3.png | Bin .../{sfw => }/L1_Cry_128x64/frame_4.png | Bin .../{sfw => }/L1_Cry_128x64/frame_5.png | Bin .../{sfw => }/L1_Cry_128x64/frame_6.png | Bin .../{sfw => }/L1_Cry_128x64/frame_7.png | Bin .../external/{sfw => }/L1_Cry_128x64/meta.txt | 0 .../{sfw => }/L1_Furippa1_128x64/frame_0.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_1.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_10.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_11.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_12.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_13.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_14.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_15.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_16.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_17.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_18.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_2.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_3.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_4.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_5.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_6.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_7.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_8.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_9.png | Bin .../{sfw => }/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.png | Bin .../L1_Happy_holidays_128x64/frame_1.png | Bin .../L1_Happy_holidays_128x64/frame_10.png | Bin .../L1_Happy_holidays_128x64/frame_11.png | Bin .../L1_Happy_holidays_128x64/frame_12.png | Bin .../L1_Happy_holidays_128x64/frame_2.png | Bin .../L1_Happy_holidays_128x64/frame_3.png | Bin .../L1_Happy_holidays_128x64/frame_4.png | Bin .../L1_Happy_holidays_128x64/frame_5.png | Bin .../L1_Happy_holidays_128x64/frame_6.png | Bin .../L1_Happy_holidays_128x64/frame_7.png | Bin .../L1_Happy_holidays_128x64/frame_8.png | Bin .../L1_Happy_holidays_128x64/frame_9.png | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{sfw => }/L1_Laptop_128x51/frame_0.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_1.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_2.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_3.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_4.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_5.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_6.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_7.png | Bin .../{sfw => }/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.png | Bin .../L1_Leaving_sad_128x64/frame_1.png | Bin .../L1_Leaving_sad_128x64/frame_10.png | Bin .../L1_Leaving_sad_128x64/frame_11.png | Bin .../L1_Leaving_sad_128x64/frame_12.png | Bin .../L1_Leaving_sad_128x64/frame_2.png | Bin .../L1_Leaving_sad_128x64/frame_3.png | Bin .../L1_Leaving_sad_128x64/frame_4.png | Bin .../L1_Leaving_sad_128x64/frame_5.png | Bin .../L1_Leaving_sad_128x64/frame_6.png | Bin .../L1_Leaving_sad_128x64/frame_7.png | Bin .../L1_Leaving_sad_128x64/frame_8.png | Bin .../L1_Leaving_sad_128x64/frame_9.png | Bin .../{sfw => }/L1_Leaving_sad_128x64/meta.txt | 0 .../{sfw => }/L1_Mad_fist_128x64/frame_0.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_1.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_10.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_11.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_12.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_13.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_2.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_3.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_4.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_5.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_6.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_7.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_8.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_9.png | Bin .../{sfw => }/L1_Mad_fist_128x64/meta.txt | 0 .../{sfw => }/L1_Mods_128x64/frame_0.png | Bin .../{sfw => }/L1_Mods_128x64/frame_1.png | Bin .../{sfw => }/L1_Mods_128x64/frame_10.png | Bin .../{sfw => }/L1_Mods_128x64/frame_11.png | Bin .../{sfw => }/L1_Mods_128x64/frame_12.png | Bin .../{sfw => }/L1_Mods_128x64/frame_13.png | Bin .../{sfw => }/L1_Mods_128x64/frame_14.png | Bin .../{sfw => }/L1_Mods_128x64/frame_15.png | Bin .../{sfw => }/L1_Mods_128x64/frame_16.png | Bin .../{sfw => }/L1_Mods_128x64/frame_17.png | Bin .../{sfw => }/L1_Mods_128x64/frame_18.png | Bin .../{sfw => }/L1_Mods_128x64/frame_19.png | Bin .../{sfw => }/L1_Mods_128x64/frame_2.png | Bin .../{sfw => }/L1_Mods_128x64/frame_20.png | Bin .../{sfw => }/L1_Mods_128x64/frame_21.png | Bin .../{sfw => }/L1_Mods_128x64/frame_22.png | Bin .../{sfw => }/L1_Mods_128x64/frame_23.png | Bin .../{sfw => }/L1_Mods_128x64/frame_24.png | Bin .../{sfw => }/L1_Mods_128x64/frame_25.png | Bin .../{sfw => }/L1_Mods_128x64/frame_26.png | Bin .../{sfw => }/L1_Mods_128x64/frame_27.png | Bin .../{sfw => }/L1_Mods_128x64/frame_28.png | Bin .../{sfw => }/L1_Mods_128x64/frame_29.png | Bin .../{sfw => }/L1_Mods_128x64/frame_3.png | Bin .../{sfw => }/L1_Mods_128x64/frame_30.png | Bin .../{sfw => }/L1_Mods_128x64/frame_31.png | Bin .../{sfw => }/L1_Mods_128x64/frame_32.png | Bin .../{sfw => }/L1_Mods_128x64/frame_33.png | Bin .../{sfw => }/L1_Mods_128x64/frame_34.png | Bin .../{sfw => }/L1_Mods_128x64/frame_35.png | Bin .../{sfw => }/L1_Mods_128x64/frame_36.png | Bin .../{sfw => }/L1_Mods_128x64/frame_37.png | Bin .../{sfw => }/L1_Mods_128x64/frame_38.png | Bin .../{sfw => }/L1_Mods_128x64/frame_39.png | Bin .../{sfw => }/L1_Mods_128x64/frame_4.png | Bin .../{sfw => }/L1_Mods_128x64/frame_40.png | Bin .../{sfw => }/L1_Mods_128x64/frame_5.png | Bin .../{sfw => }/L1_Mods_128x64/frame_6.png | Bin .../{sfw => }/L1_Mods_128x64/frame_7.png | Bin .../{sfw => }/L1_Mods_128x64/frame_8.png | Bin .../{sfw => }/L1_Mods_128x64/frame_9.png | Bin .../{sfw => }/L1_Mods_128x64/meta.txt | 0 .../{sfw => }/L1_Painting_128x64/frame_0.png | Bin .../{sfw => }/L1_Painting_128x64/frame_1.png | Bin .../{sfw => }/L1_Painting_128x64/frame_10.png | Bin .../{sfw => }/L1_Painting_128x64/frame_11.png | Bin .../{sfw => }/L1_Painting_128x64/frame_2.png | Bin .../{sfw => }/L1_Painting_128x64/frame_3.png | Bin .../{sfw => }/L1_Painting_128x64/frame_4.png | Bin .../{sfw => }/L1_Painting_128x64/frame_5.png | Bin .../{sfw => }/L1_Painting_128x64/frame_6.png | Bin .../{sfw => }/L1_Painting_128x64/frame_7.png | Bin .../{sfw => }/L1_Painting_128x64/frame_8.png | Bin .../{sfw => }/L1_Painting_128x64/frame_9.png | Bin .../{sfw => }/L1_Painting_128x64/meta.txt | 0 .../L1_Read_books_128x64/frame_0.png | Bin .../L1_Read_books_128x64/frame_1.png | Bin .../L1_Read_books_128x64/frame_2.png | Bin .../L1_Read_books_128x64/frame_3.png | Bin .../L1_Read_books_128x64/frame_4.png | Bin .../L1_Read_books_128x64/frame_5.png | Bin .../L1_Read_books_128x64/frame_6.png | Bin .../L1_Read_books_128x64/frame_7.png | Bin .../L1_Read_books_128x64/frame_8.png | Bin .../{sfw => }/L1_Read_books_128x64/meta.txt | 0 .../{sfw => }/L1_Recording_128x51/frame_0.png | Bin .../{sfw => }/L1_Recording_128x51/frame_1.png | Bin .../L1_Recording_128x51/frame_10.png | Bin .../L1_Recording_128x51/frame_11.png | Bin .../{sfw => }/L1_Recording_128x51/frame_2.png | Bin .../{sfw => }/L1_Recording_128x51/frame_3.png | Bin .../{sfw => }/L1_Recording_128x51/frame_4.png | Bin .../{sfw => }/L1_Recording_128x51/frame_5.png | Bin .../{sfw => }/L1_Recording_128x51/frame_6.png | Bin .../{sfw => }/L1_Recording_128x51/frame_7.png | Bin .../{sfw => }/L1_Recording_128x51/frame_8.png | Bin .../{sfw => }/L1_Recording_128x51/frame_9.png | Bin .../{sfw => }/L1_Recording_128x51/meta.txt | 0 .../{sfw => }/L1_Sleep_128x64/frame_0.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_1.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_2.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_3.png | Bin .../{sfw => }/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.png | Bin .../L1_Sleigh_ride_128x64/frame_1.png | Bin .../L1_Sleigh_ride_128x64/frame_10.png | Bin .../L1_Sleigh_ride_128x64/frame_11.png | Bin .../L1_Sleigh_ride_128x64/frame_12.png | Bin .../L1_Sleigh_ride_128x64/frame_13.png | Bin .../L1_Sleigh_ride_128x64/frame_14.png | Bin .../L1_Sleigh_ride_128x64/frame_15.png | Bin .../L1_Sleigh_ride_128x64/frame_16.png | Bin .../L1_Sleigh_ride_128x64/frame_17.png | Bin .../L1_Sleigh_ride_128x64/frame_18.png | Bin .../L1_Sleigh_ride_128x64/frame_19.png | Bin .../L1_Sleigh_ride_128x64/frame_2.png | Bin .../L1_Sleigh_ride_128x64/frame_20.png | Bin .../L1_Sleigh_ride_128x64/frame_21.png | Bin .../L1_Sleigh_ride_128x64/frame_22.png | Bin .../L1_Sleigh_ride_128x64/frame_23.png | Bin .../L1_Sleigh_ride_128x64/frame_24.png | Bin .../L1_Sleigh_ride_128x64/frame_25.png | Bin .../L1_Sleigh_ride_128x64/frame_26.png | Bin .../L1_Sleigh_ride_128x64/frame_27.png | Bin .../L1_Sleigh_ride_128x64/frame_28.png | Bin .../L1_Sleigh_ride_128x64/frame_29.png | Bin .../L1_Sleigh_ride_128x64/frame_3.png | Bin .../L1_Sleigh_ride_128x64/frame_30.png | Bin .../L1_Sleigh_ride_128x64/frame_31.png | Bin .../L1_Sleigh_ride_128x64/frame_32.png | Bin .../L1_Sleigh_ride_128x64/frame_33.png | Bin .../L1_Sleigh_ride_128x64/frame_34.png | Bin .../L1_Sleigh_ride_128x64/frame_35.png | Bin .../L1_Sleigh_ride_128x64/frame_36.png | Bin .../L1_Sleigh_ride_128x64/frame_4.png | Bin .../L1_Sleigh_ride_128x64/frame_5.png | Bin .../L1_Sleigh_ride_128x64/frame_6.png | Bin .../L1_Sleigh_ride_128x64/frame_7.png | Bin .../L1_Sleigh_ride_128x64/frame_8.png | Bin .../L1_Sleigh_ride_128x64/frame_9.png | Bin .../{sfw => }/L1_Sleigh_ride_128x64/meta.txt | 0 .../{sfw => }/L1_Waves_128x50/frame_0.png | Bin .../{sfw => }/L1_Waves_128x50/frame_1.png | Bin .../{sfw => }/L1_Waves_128x50/frame_2.png | Bin .../{sfw => }/L1_Waves_128x50/frame_3.png | Bin .../{sfw => }/L1_Waves_128x50/meta.txt | 0 .../{sfw => }/L2_Furippa2_128x64/frame_0.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_1.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_10.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_11.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_12.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_13.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_14.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_15.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_16.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_17.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_18.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_2.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_3.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_4.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_5.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_6.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_7.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_8.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_9.png | Bin .../{sfw => }/L2_Furippa2_128x64/meta.txt | 0 .../L2_Hacking_pc_128x64/frame_0.png | Bin .../L2_Hacking_pc_128x64/frame_1.png | Bin .../L2_Hacking_pc_128x64/frame_2.png | Bin .../L2_Hacking_pc_128x64/frame_3.png | Bin .../L2_Hacking_pc_128x64/frame_4.png | Bin .../{sfw => }/L2_Hacking_pc_128x64/meta.txt | 0 .../{sfw => }/L2_Soldering_128x64/frame_0.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_1.png | Bin .../L2_Soldering_128x64/frame_10.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_2.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_3.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_4.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_5.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_6.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_7.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_8.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_9.png | Bin .../{sfw => }/L2_Soldering_128x64/meta.txt | 0 .../{sfw => }/L2_Wake_up_128x64/frame_0.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_1.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_10.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_11.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_12.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_13.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_14.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_15.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_16.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_17.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_18.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_19.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_2.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_20.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_3.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_4.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_5.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_6.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_7.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_8.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_9.png | Bin .../{sfw => }/L2_Wake_up_128x64/meta.txt | 0 .../{sfw => }/L3_Furippa3_128x64/frame_0.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_1.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_10.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_11.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_12.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_13.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_14.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_15.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_16.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_17.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_18.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_2.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_3.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_4.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_5.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_6.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_7.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_8.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_9.png | Bin .../{sfw => }/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.png | Bin .../L3_Hijack_radio_128x64/frame_1.png | Bin .../L3_Hijack_radio_128x64/frame_10.png | Bin .../L3_Hijack_radio_128x64/frame_11.png | Bin .../L3_Hijack_radio_128x64/frame_12.png | Bin .../L3_Hijack_radio_128x64/frame_13.png | Bin .../L3_Hijack_radio_128x64/frame_2.png | Bin .../L3_Hijack_radio_128x64/frame_3.png | Bin .../L3_Hijack_radio_128x64/frame_4.png | Bin .../L3_Hijack_radio_128x64/frame_5.png | Bin .../L3_Hijack_radio_128x64/frame_6.png | Bin .../L3_Hijack_radio_128x64/frame_7.png | Bin .../L3_Hijack_radio_128x64/frame_8.png | Bin .../L3_Hijack_radio_128x64/frame_9.png | Bin .../{sfw => }/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.png | Bin .../L3_Lab_research_128x54/frame_1.png | Bin .../L3_Lab_research_128x54/frame_10.png | Bin .../L3_Lab_research_128x54/frame_11.png | Bin .../L3_Lab_research_128x54/frame_12.png | Bin .../L3_Lab_research_128x54/frame_13.png | Bin .../L3_Lab_research_128x54/frame_2.png | Bin .../L3_Lab_research_128x54/frame_3.png | Bin .../L3_Lab_research_128x54/frame_4.png | Bin .../L3_Lab_research_128x54/frame_5.png | Bin .../L3_Lab_research_128x54/frame_6.png | Bin .../L3_Lab_research_128x54/frame_7.png | Bin .../L3_Lab_research_128x54/frame_8.png | Bin .../L3_Lab_research_128x54/frame_9.png | Bin .../{sfw => }/L3_Lab_research_128x54/meta.txt | 0 .../dolphin/external/{sfw => }/manifest.txt | 0 .../Levelup1_128x64_sfw/frame_00.png | Bin 1326 -> 0 bytes .../Levelup1_128x64_sfw/frame_01.png | Bin 1597 -> 0 bytes .../Levelup1_128x64_sfw/frame_02.png | Bin 1754 -> 0 bytes .../Levelup1_128x64_sfw/frame_03.png | Bin 1828 -> 0 bytes .../Levelup1_128x64_sfw/frame_04.png | Bin 1686 -> 0 bytes .../Levelup1_128x64_sfw/frame_05.png | Bin 1672 -> 0 bytes .../Levelup1_128x64_sfw/frame_06.png | Bin 1659 -> 0 bytes .../Levelup1_128x64_sfw/frame_07.png | Bin 1540 -> 0 bytes .../Levelup1_128x64_sfw/frame_08.png | Bin 1557 -> 0 bytes .../Levelup1_128x64_sfw/frame_09.png | Bin 1551 -> 0 bytes .../Levelup1_128x64_sfw/frame_10.png | Bin 1604 -> 0 bytes .../Animations/Levelup2_128x64_sfw/frame_rate | 1 - .../frame_00.png | Bin .../frame_01.png | Bin .../frame_02.png | Bin .../frame_03.png | Bin .../frame_04.png | Bin .../frame_05.png | Bin .../frame_06.png | Bin .../frame_07.png | Bin .../frame_08.png | Bin .../frame_09.png | Bin .../frame_10.png | Bin .../frame_rate | 0 assets/icons/BLE/BLE_Pairing_128x64.png | Bin 2610 -> 2307 bytes assets/icons/BLE/BLE_Pairing_128x64_sfw.png | Bin 2307 -> 0 bytes assets/icons/Dolphin/DolphinCommon_56x48.png | Bin 3376 -> 1416 bytes .../icons/Dolphin/DolphinCommon_56x48_sfw.png | Bin 1416 -> 0 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 5390 -> 1177 bytes .../DolphinReadingSuccess_59x63_sfw.png | Bin 1177 -> 0 bytes assets/icons/Interface/SmallArrowDown_4x7.png | Bin 8340 -> 0 bytes assets/icons/Interface/SmallArrowUp_4x7.png | Bin 8552 -> 0 bytes assets/icons/NFC/ArrowC_1_36x36.png | Bin 3692 -> 0 bytes assets/icons/NFC/Detailed_chip_17x13.png | Bin 981 -> 0 bytes assets/icons/NFC/Medium-chip-22x21.png | Bin 3740 -> 0 bytes .../icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 4224 -> 1541 bytes .../NFC/NFC_dolphin_emulation_47x61_sfw.png | Bin 1541 -> 0 bytes assets/icons/NFC/Tap_reader_36x38.png | Bin 3748 -> 0 bytes assets/icons/Passport/passport_DB.png | Bin 852 -> 750 bytes assets/icons/Passport/passport_DB_sfw.png | Bin 750 -> 0 bytes .../Passport/passport_bad1_46x49_sfw.png | Bin 1237 -> 0 bytes .../Passport/passport_bad3_46x49_sfw.png | Bin 1304 -> 0 bytes ...2_46x49_sfw.png => passport_bad_46x49.png} | Bin .../Passport/passport_happy1_46x49_sfw.png | Bin 1296 -> 0 bytes .../Passport/passport_happy3_46x49_sfw.png | Bin 1348 -> 0 bytes ...46x49_sfw.png => passport_happy_46x49.png} | Bin .../Passport/passport_okay1_46x49_sfw.png | Bin 1244 -> 0 bytes .../Passport/passport_okay3_46x49_sfw.png | Bin 1304 -> 0 bytes ..._46x49_sfw.png => passport_okay_46x49.png} | Bin .../icons/RFID/RFIDDolphinReceive_97x61.png | Bin 4862 -> 1421 bytes .../RFID/RFIDDolphinReceive_97x61_sfw.png | Bin 1421 -> 0 bytes assets/icons/RFID/RFIDDolphinSend_97x61.png | Bin 4882 -> 1418 bytes .../icons/RFID/RFIDDolphinSend_97x61_sfw.png | Bin 1418 -> 0 bytes .../icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 4466 -> 2681 bytes .../RFID/RFIDDolphinSuccess_108x57_sfw.png | Bin 2681 -> 0 bytes assets/icons/Settings/Cry_dolph_55x52.png | Bin 3798 -> 3898 bytes assets/icons/Settings/Cry_dolph_55x52_sfw.png | Bin 3898 -> 0 bytes assets/icons/StatusBar/Alert_9x8.png | Bin 3611 -> 0 bytes assets/icons/StatusBar/Attention_5x8.png | Bin 1690 -> 0 bytes ...g_9x10.png => Charging_lightning_9x10.png} | Bin ...0.png => Charging_lightning_mask_9x10.png} | Bin assets/icons/StatusBar/GameMode_11x8.png | Bin 3610 -> 0 bytes assets/icons/SubGhz/Scanning_123x52.png | Bin 4092 -> 1690 bytes assets/icons/SubGhz/Scanning_123x52_sfw.png | Bin 1690 -> 0 bytes assets/icons/U2F/Auth_62x31.png | Bin 1864 -> 3761 bytes assets/icons/U2F/Auth_62x31_sfw.png | Bin 3761 -> 0 bytes assets/icons/U2F/Connect_me_62x31.png | Bin 1895 -> 3767 bytes assets/icons/U2F/Connect_me_62x31_sfw.png | Bin 3767 -> 0 bytes assets/icons/U2F/Connected_62x31.png | Bin 1874 -> 3765 bytes assets/icons/U2F/Connected_62x31_sfw.png | Bin 3765 -> 0 bytes assets/icons/U2F/Error_62x31.png | Bin 1863 -> 3751 bytes assets/icons/U2F/Error_62x31_sfw.png | Bin 3751 -> 0 bytes assets/icons/iButton/DolphinMafia_115x62.png | Bin 6876 -> 2504 bytes .../icons/iButton/DolphinMafia_115x62_sfw.png | Bin 2504 -> 0 bytes assets/icons/iButton/DolphinNice_96x59.png | Bin 5422 -> 2459 bytes .../icons/iButton/DolphinNice_96x59_sfw.png | Bin 2459 -> 0 bytes assets/icons/iButton/DolphinWait_61x59.png | Bin 5122 -> 2023 bytes .../icons/iButton/DolphinWait_61x59_sfw.png | Bin 2023 -> 0 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 4719 -> 2157 bytes .../iButtonDolphinVerySuccess_108x52_sfw.png | Bin 2157 -> 0 bytes .../{sfw => }/L1_Boxing_128x64/frame_0.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_1.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_2.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_3.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_4.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_5.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_6.bm | Bin .../{sfw => }/L1_Boxing_128x64/meta.txt | 0 .../{sfw => }/L1_Cry_128x64/frame_0.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_1.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_2.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_3.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_4.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_5.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_6.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_7.bm | Bin .../dolphin/{sfw => }/L1_Cry_128x64/meta.txt | 0 .../{sfw => }/L1_Furippa1_128x64/frame_0.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_1.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_10.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_11.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_12.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_13.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_14.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_15.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_16.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_17.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_18.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_2.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_3.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_4.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_5.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_6.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_7.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_8.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_9.bm | Bin .../{sfw => }/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.bm | Bin .../L1_Happy_holidays_128x64/frame_1.bm | Bin .../L1_Happy_holidays_128x64/frame_10.bm | Bin .../L1_Happy_holidays_128x64/frame_11.bm | Bin .../L1_Happy_holidays_128x64/frame_12.bm | Bin .../L1_Happy_holidays_128x64/frame_2.bm | Bin .../L1_Happy_holidays_128x64/frame_3.bm | Bin .../L1_Happy_holidays_128x64/frame_4.bm | Bin .../L1_Happy_holidays_128x64/frame_5.bm | Bin .../L1_Happy_holidays_128x64/frame_6.bm | Bin .../L1_Happy_holidays_128x64/frame_7.bm | Bin .../L1_Happy_holidays_128x64/frame_8.bm | Bin .../L1_Happy_holidays_128x64/frame_9.bm | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{sfw => }/L1_Laptop_128x51/frame_0.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_1.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_2.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_3.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_4.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_5.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_6.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_7.bm | Bin .../{sfw => }/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.bm | Bin .../L1_Leaving_sad_128x64/frame_1.bm | Bin .../L1_Leaving_sad_128x64/frame_10.bm | Bin .../L1_Leaving_sad_128x64/frame_11.bm | Bin .../L1_Leaving_sad_128x64/frame_12.bm | Bin .../L1_Leaving_sad_128x64/frame_2.bm | Bin .../L1_Leaving_sad_128x64/frame_3.bm | Bin .../L1_Leaving_sad_128x64/frame_4.bm | Bin .../L1_Leaving_sad_128x64/frame_5.bm | Bin .../L1_Leaving_sad_128x64/frame_6.bm | Bin .../L1_Leaving_sad_128x64/frame_7.bm | Bin .../L1_Leaving_sad_128x64/frame_8.bm | Bin .../L1_Leaving_sad_128x64/frame_9.bm | Bin .../{sfw => }/L1_Leaving_sad_128x64/meta.txt | 0 .../{sfw => }/L1_Mad_fist_128x64/frame_0.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_1.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_10.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_11.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_12.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_13.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_2.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_3.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_4.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_5.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_6.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_7.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_8.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_9.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/meta.txt | 0 .../{sfw => }/L1_Mods_128x64/frame_0.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_1.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_10.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_11.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_12.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_13.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_14.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_15.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_16.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_17.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_18.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_19.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_2.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_20.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_21.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_22.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_23.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_24.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_25.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_26.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_27.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_28.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_29.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_3.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_30.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_31.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_32.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_33.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_34.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_35.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_36.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_37.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_38.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_39.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_4.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_40.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_5.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_6.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_7.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_8.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_9.bm | Bin .../dolphin/{sfw => }/L1_Mods_128x64/meta.txt | 0 .../{sfw => }/L1_Painting_128x64/frame_0.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_1.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_10.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_11.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_2.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_3.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_4.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_5.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_6.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_7.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_8.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_9.bm | Bin .../{sfw => }/L1_Painting_128x64/meta.txt | 0 .../{sfw => }/L1_Read_books_128x64/frame_0.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_1.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_2.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_3.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_4.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_5.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_6.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_7.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_8.bm | Bin .../{sfw => }/L1_Read_books_128x64/meta.txt | 0 .../{sfw => }/L1_Recording_128x51/frame_0.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_1.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_10.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_11.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_2.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_3.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_4.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_5.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_6.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_7.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_8.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_9.bm | Bin .../{sfw => }/L1_Recording_128x51/meta.txt | 0 .../{sfw => }/L1_Sleep_128x64/frame_0.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_1.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_2.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_3.bm | Bin .../{sfw => }/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.bm | Bin .../L1_Sleigh_ride_128x64/frame_1.bm | Bin .../L1_Sleigh_ride_128x64/frame_10.bm | Bin .../L1_Sleigh_ride_128x64/frame_11.bm | Bin .../L1_Sleigh_ride_128x64/frame_12.bm | Bin .../L1_Sleigh_ride_128x64/frame_13.bm | Bin .../L1_Sleigh_ride_128x64/frame_14.bm | Bin .../L1_Sleigh_ride_128x64/frame_15.bm | Bin .../L1_Sleigh_ride_128x64/frame_16.bm | Bin .../L1_Sleigh_ride_128x64/frame_17.bm | Bin .../L1_Sleigh_ride_128x64/frame_18.bm | Bin .../L1_Sleigh_ride_128x64/frame_19.bm | Bin .../L1_Sleigh_ride_128x64/frame_2.bm | Bin .../L1_Sleigh_ride_128x64/frame_20.bm | Bin .../L1_Sleigh_ride_128x64/frame_21.bm | Bin .../L1_Sleigh_ride_128x64/frame_22.bm | Bin .../L1_Sleigh_ride_128x64/frame_23.bm | Bin .../L1_Sleigh_ride_128x64/frame_24.bm | Bin .../L1_Sleigh_ride_128x64/frame_25.bm | Bin .../L1_Sleigh_ride_128x64/frame_26.bm | Bin .../L1_Sleigh_ride_128x64/frame_27.bm | Bin .../L1_Sleigh_ride_128x64/frame_28.bm | Bin .../L1_Sleigh_ride_128x64/frame_29.bm | Bin .../L1_Sleigh_ride_128x64/frame_3.bm | Bin .../L1_Sleigh_ride_128x64/frame_30.bm | Bin .../L1_Sleigh_ride_128x64/frame_31.bm | Bin .../L1_Sleigh_ride_128x64/frame_32.bm | Bin .../L1_Sleigh_ride_128x64/frame_33.bm | Bin .../L1_Sleigh_ride_128x64/frame_34.bm | Bin .../L1_Sleigh_ride_128x64/frame_35.bm | Bin .../L1_Sleigh_ride_128x64/frame_36.bm | Bin .../L1_Sleigh_ride_128x64/frame_4.bm | Bin .../L1_Sleigh_ride_128x64/frame_5.bm | Bin .../L1_Sleigh_ride_128x64/frame_6.bm | Bin .../L1_Sleigh_ride_128x64/frame_7.bm | Bin .../L1_Sleigh_ride_128x64/frame_8.bm | Bin .../L1_Sleigh_ride_128x64/frame_9.bm | Bin .../{sfw => }/L1_Sleigh_ride_128x64/meta.txt | 0 .../{sfw => }/L1_Waves_128x50/frame_0.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_1.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_2.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_3.bm | Bin .../{sfw => }/L1_Waves_128x50/meta.txt | 0 .../{sfw => }/L2_Furippa2_128x64/frame_0.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_1.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_10.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_11.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_12.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_13.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_14.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_15.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_16.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_17.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_18.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_2.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_3.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_4.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_5.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_6.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_7.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_8.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_9.bm | Bin .../{sfw => }/L2_Furippa2_128x64/meta.txt | 0 .../{sfw => }/L2_Hacking_pc_128x64/frame_0.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_1.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_2.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_3.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_4.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/meta.txt | 0 .../{sfw => }/L2_Soldering_128x64/frame_0.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_1.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_10.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_2.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_3.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_4.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_5.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_6.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_7.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_8.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_9.bm | Bin .../{sfw => }/L2_Soldering_128x64/meta.txt | 0 .../{sfw => }/L2_Wake_up_128x64/frame_0.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_1.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_10.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_11.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_12.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_13.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_14.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_15.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_16.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_17.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_18.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_19.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_2.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_20.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_3.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_4.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_5.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_6.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_7.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_8.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_9.bm | Bin .../{sfw => }/L2_Wake_up_128x64/meta.txt | 0 .../{sfw => }/L3_Furippa3_128x64/frame_0.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_1.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_10.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_11.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_12.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_13.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_14.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_15.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_16.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_17.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_18.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_2.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_3.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_4.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_5.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_6.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_7.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_8.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_9.bm | Bin .../{sfw => }/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.bm | Bin .../L3_Hijack_radio_128x64/frame_1.bm | Bin .../L3_Hijack_radio_128x64/frame_10.bm | Bin .../L3_Hijack_radio_128x64/frame_11.bm | Bin .../L3_Hijack_radio_128x64/frame_12.bm | Bin .../L3_Hijack_radio_128x64/frame_13.bm | Bin .../L3_Hijack_radio_128x64/frame_2.bm | Bin .../L3_Hijack_radio_128x64/frame_3.bm | Bin .../L3_Hijack_radio_128x64/frame_4.bm | Bin .../L3_Hijack_radio_128x64/frame_5.bm | Bin .../L3_Hijack_radio_128x64/frame_6.bm | Bin .../L3_Hijack_radio_128x64/frame_7.bm | Bin .../L3_Hijack_radio_128x64/frame_8.bm | Bin .../L3_Hijack_radio_128x64/frame_9.bm | Bin .../{sfw => }/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.bm | Bin .../L3_Lab_research_128x54/frame_1.bm | Bin .../L3_Lab_research_128x54/frame_10.bm | Bin .../L3_Lab_research_128x54/frame_11.bm | Bin .../L3_Lab_research_128x54/frame_12.bm | Bin .../L3_Lab_research_128x54/frame_13.bm | Bin .../L3_Lab_research_128x54/frame_2.bm | Bin .../L3_Lab_research_128x54/frame_3.bm | Bin .../L3_Lab_research_128x54/frame_4.bm | Bin .../L3_Lab_research_128x54/frame_5.bm | Bin .../L3_Lab_research_128x54/frame_6.bm | Bin .../L3_Lab_research_128x54/frame_7.bm | Bin .../L3_Lab_research_128x54/frame_8.bm | Bin .../L3_Lab_research_128x54/frame_9.bm | Bin .../{sfw => }/L3_Lab_research_128x54/meta.txt | 0 .../resources/dolphin/{sfw => }/manifest.txt | 0 .../Anims}/PaxGod_TikTok_Marketing/frame_0.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_1.bm | Bin .../PaxGod_TikTok_Marketing/frame_10.bm | Bin .../PaxGod_TikTok_Marketing/frame_11.bm | Bin .../PaxGod_TikTok_Marketing/frame_12.bm | Bin .../PaxGod_TikTok_Marketing/frame_13.bm | Bin .../PaxGod_TikTok_Marketing/frame_14.bm | Bin .../PaxGod_TikTok_Marketing/frame_15.bm | Bin .../PaxGod_TikTok_Marketing/frame_16.bm | Bin .../PaxGod_TikTok_Marketing/frame_17.bm | Bin .../PaxGod_TikTok_Marketing/frame_18.bm | Bin .../PaxGod_TikTok_Marketing/frame_19.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_2.bm | Bin .../PaxGod_TikTok_Marketing/frame_20.bm | Bin .../PaxGod_TikTok_Marketing/frame_21.bm | Bin .../Anims/PaxGod_TikTok_Marketing/frame_22.bm | Bin 0 -> 472 bytes .../Anims/PaxGod_TikTok_Marketing/frame_23.bm | Bin 0 -> 462 bytes .../Anims/PaxGod_TikTok_Marketing/frame_24.bm | Bin 0 -> 487 bytes .../Anims/PaxGod_TikTok_Marketing/frame_25.bm | Bin 0 -> 483 bytes .../Anims/PaxGod_TikTok_Marketing/frame_26.bm | Bin 0 -> 483 bytes .../Anims/PaxGod_TikTok_Marketing/frame_27.bm | Bin 0 -> 488 bytes .../Anims}/PaxGod_TikTok_Marketing/frame_3.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_4.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_5.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_6.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_7.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_8.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_9.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/meta.txt | 3 +- .../NSFW/Anims}/lvl_1/frame_0.bm | Bin .../NSFW/Anims}/lvl_1/frame_1.bm | Bin .../NSFW/Anims}/lvl_1/frame_10.bm | Bin .../NSFW/Anims}/lvl_1/frame_11.bm | Bin .../NSFW/Anims}/lvl_1/frame_12.bm | Bin .../NSFW/Anims}/lvl_1/frame_13.bm | Bin .../NSFW/Anims}/lvl_1/frame_14.bm | Bin .../NSFW/Anims}/lvl_1/frame_15.bm | Bin .../NSFW/Anims}/lvl_1/frame_16.bm | Bin .../NSFW/Anims}/lvl_1/frame_17.bm | Bin .../NSFW/Anims}/lvl_1/frame_18.bm | Bin .../NSFW/Anims}/lvl_1/frame_19.bm | Bin .../NSFW/Anims}/lvl_1/frame_2.bm | Bin .../NSFW/Anims}/lvl_1/frame_20.bm | Bin .../NSFW/Anims}/lvl_1/frame_21.bm | Bin .../NSFW/Anims}/lvl_1/frame_22.bm | Bin .../NSFW/Anims}/lvl_1/frame_23.bm | Bin .../NSFW/Anims}/lvl_1/frame_24.bm | Bin .../NSFW/Anims}/lvl_1/frame_25.bm | Bin .../NSFW/Anims}/lvl_1/frame_26.bm | Bin .../NSFW/Anims}/lvl_1/frame_27.bm | Bin .../NSFW/Anims}/lvl_1/frame_28.bm | Bin .../NSFW/Anims}/lvl_1/frame_29.bm | Bin .../NSFW/Anims}/lvl_1/frame_3.bm | Bin .../NSFW/Anims}/lvl_1/frame_30.bm | Bin .../NSFW/Anims}/lvl_1/frame_4.bm | Bin .../NSFW/Anims}/lvl_1/frame_5.bm | Bin .../NSFW/Anims}/lvl_1/frame_6.bm | Bin .../NSFW/Anims}/lvl_1/frame_7.bm | Bin .../NSFW/Anims}/lvl_1/frame_8.bm | Bin .../NSFW/Anims}/lvl_1/frame_9.bm | Bin .../NSFW/Anims}/lvl_1/meta.txt | 0 .../NSFW/Anims}/lvl_10/frame_0.bm | Bin .../NSFW/Anims}/lvl_10/frame_1.bm | Bin .../NSFW/Anims}/lvl_10/frame_10.bm | Bin .../NSFW/Anims}/lvl_10/frame_11.bm | Bin .../NSFW/Anims}/lvl_10/frame_12.bm | Bin .../NSFW/Anims}/lvl_10/frame_13.bm | Bin .../NSFW/Anims}/lvl_10/frame_14.bm | Bin .../NSFW/Anims}/lvl_10/frame_15.bm | Bin .../NSFW/Anims}/lvl_10/frame_16.bm | Bin .../NSFW/Anims}/lvl_10/frame_17.bm | Bin .../NSFW/Anims}/lvl_10/frame_18.bm | Bin .../NSFW/Anims}/lvl_10/frame_19.bm | Bin .../NSFW/Anims}/lvl_10/frame_2.bm | Bin .../NSFW/Anims}/lvl_10/frame_20.bm | Bin .../NSFW/Anims}/lvl_10/frame_21.bm | Bin .../NSFW/Anims}/lvl_10/frame_22.bm | Bin .../NSFW/Anims}/lvl_10/frame_23.bm | Bin .../NSFW/Anims}/lvl_10/frame_24.bm | Bin .../NSFW/Anims}/lvl_10/frame_25.bm | Bin .../NSFW/Anims}/lvl_10/frame_26.bm | Bin .../NSFW/Anims}/lvl_10/frame_27.bm | Bin .../NSFW/Anims}/lvl_10/frame_3.bm | Bin .../NSFW/Anims}/lvl_10/frame_4.bm | Bin .../NSFW/Anims}/lvl_10/frame_5.bm | Bin .../NSFW/Anims}/lvl_10/frame_6.bm | Bin .../NSFW/Anims}/lvl_10/frame_7.bm | Bin .../NSFW/Anims}/lvl_10/frame_8.bm | Bin .../NSFW/Anims}/lvl_10/frame_9.bm | Bin .../NSFW/Anims}/lvl_10/meta.txt | 0 .../NSFW/Anims}/lvl_11/frame_0.bm | Bin .../NSFW/Anims}/lvl_11/frame_1.bm | Bin .../NSFW/Anims}/lvl_11/frame_10.bm | Bin .../NSFW/Anims}/lvl_11/frame_11.bm | Bin .../NSFW/Anims}/lvl_11/frame_12.bm | Bin .../NSFW/Anims}/lvl_11/frame_13.bm | Bin .../NSFW/Anims}/lvl_11/frame_14.bm | Bin .../NSFW/Anims}/lvl_11/frame_15.bm | Bin .../NSFW/Anims}/lvl_11/frame_16.bm | Bin .../NSFW/Anims}/lvl_11/frame_17.bm | Bin .../NSFW/Anims}/lvl_11/frame_18.bm | Bin .../NSFW/Anims}/lvl_11/frame_19.bm | Bin .../NSFW/Anims}/lvl_11/frame_2.bm | Bin .../NSFW/Anims}/lvl_11/frame_20.bm | Bin .../NSFW/Anims}/lvl_11/frame_21.bm | Bin .../NSFW/Anims}/lvl_11/frame_22.bm | Bin .../NSFW/Anims}/lvl_11/frame_23.bm | Bin .../NSFW/Anims}/lvl_11/frame_24.bm | Bin .../NSFW/Anims}/lvl_11/frame_25.bm | Bin .../NSFW/Anims}/lvl_11/frame_26.bm | Bin .../NSFW/Anims}/lvl_11/frame_27.bm | Bin .../NSFW/Anims}/lvl_11/frame_28.bm | Bin .../NSFW/Anims}/lvl_11/frame_29.bm | Bin .../NSFW/Anims}/lvl_11/frame_3.bm | Bin .../NSFW/Anims}/lvl_11/frame_30.bm | Bin .../NSFW/Anims}/lvl_11/frame_31.bm | Bin .../NSFW/Anims}/lvl_11/frame_32.bm | Bin .../NSFW/Anims}/lvl_11/frame_33.bm | Bin .../NSFW/Anims}/lvl_11/frame_34.bm | Bin .../NSFW/Anims}/lvl_11/frame_35.bm | Bin .../NSFW/Anims}/lvl_11/frame_36.bm | Bin .../NSFW/Anims}/lvl_11/frame_37.bm | Bin .../NSFW/Anims}/lvl_11/frame_38.bm | Bin .../NSFW/Anims}/lvl_11/frame_39.bm | Bin .../NSFW/Anims}/lvl_11/frame_4.bm | Bin .../NSFW/Anims}/lvl_11/frame_40.bm | Bin .../NSFW/Anims}/lvl_11/frame_41.bm | Bin .../NSFW/Anims}/lvl_11/frame_42.bm | Bin .../NSFW/Anims}/lvl_11/frame_43.bm | Bin .../NSFW/Anims}/lvl_11/frame_44.bm | Bin .../NSFW/Anims}/lvl_11/frame_45.bm | Bin .../NSFW/Anims}/lvl_11/frame_46.bm | Bin .../NSFW/Anims}/lvl_11/frame_47.bm | Bin .../NSFW/Anims}/lvl_11/frame_48.bm | Bin .../NSFW/Anims}/lvl_11/frame_49.bm | Bin .../NSFW/Anims}/lvl_11/frame_5.bm | Bin .../NSFW/Anims}/lvl_11/frame_6.bm | Bin .../NSFW/Anims}/lvl_11/frame_7.bm | Bin .../NSFW/Anims}/lvl_11/frame_8.bm | Bin .../NSFW/Anims}/lvl_11/frame_9.bm | Bin .../NSFW/Anims}/lvl_11/meta.txt | 0 .../NSFW/Anims}/lvl_12/frame_0.bm | Bin .../NSFW/Anims}/lvl_12/frame_1.bm | Bin .../NSFW/Anims}/lvl_12/frame_10.bm | Bin .../NSFW/Anims}/lvl_12/frame_11.bm | Bin .../NSFW/Anims}/lvl_12/frame_12.bm | Bin .../NSFW/Anims}/lvl_12/frame_13.bm | Bin .../NSFW/Anims}/lvl_12/frame_14.bm | Bin .../NSFW/Anims}/lvl_12/frame_15.bm | Bin .../NSFW/Anims}/lvl_12/frame_2.bm | Bin .../NSFW/Anims}/lvl_12/frame_3.bm | Bin .../NSFW/Anims}/lvl_12/frame_4.bm | Bin .../NSFW/Anims}/lvl_12/frame_5.bm | Bin .../NSFW/Anims}/lvl_12/frame_6.bm | Bin .../NSFW/Anims}/lvl_12/frame_7.bm | Bin .../NSFW/Anims}/lvl_12/frame_8.bm | Bin .../NSFW/Anims}/lvl_12/frame_9.bm | Bin .../NSFW/Anims}/lvl_12/meta.txt | 0 .../NSFW/Anims}/lvl_13/frame_0.bm | Bin .../NSFW/Anims}/lvl_13/frame_1.bm | Bin .../NSFW/Anims}/lvl_13/frame_10.bm | Bin .../NSFW/Anims}/lvl_13/frame_2.bm | Bin .../NSFW/Anims}/lvl_13/frame_3.bm | Bin .../NSFW/Anims}/lvl_13/frame_4.bm | Bin .../NSFW/Anims}/lvl_13/frame_5.bm | Bin .../NSFW/Anims}/lvl_13/frame_6.bm | Bin .../NSFW/Anims}/lvl_13/frame_7.bm | Bin .../NSFW/Anims}/lvl_13/frame_8.bm | Bin .../NSFW/Anims}/lvl_13/frame_9.bm | Bin .../NSFW/Anims}/lvl_13/meta.txt | 0 .../NSFW/Anims}/lvl_14/frame_0.bm | Bin .../NSFW/Anims}/lvl_14/frame_1.bm | Bin .../NSFW/Anims}/lvl_14/frame_2.bm | Bin .../NSFW/Anims}/lvl_14/frame_3.bm | Bin .../NSFW/Anims}/lvl_14/frame_4.bm | Bin .../NSFW/Anims}/lvl_14/frame_5.bm | Bin .../NSFW/Anims}/lvl_14/frame_6.bm | Bin .../NSFW/Anims}/lvl_14/frame_7.bm | Bin .../NSFW/Anims}/lvl_14/meta.txt | 0 .../NSFW/Anims}/lvl_15/frame_0.bm | Bin .../NSFW/Anims}/lvl_15/frame_1.bm | Bin .../NSFW/Anims}/lvl_15/frame_10.bm | Bin .../NSFW/Anims}/lvl_15/frame_11.bm | Bin .../NSFW/Anims}/lvl_15/frame_12.bm | Bin .../NSFW/Anims}/lvl_15/frame_13.bm | Bin .../NSFW/Anims}/lvl_15/frame_14.bm | Bin .../NSFW/Anims}/lvl_15/frame_15.bm | Bin .../NSFW/Anims}/lvl_15/frame_16.bm | Bin .../NSFW/Anims}/lvl_15/frame_17.bm | Bin .../NSFW/Anims}/lvl_15/frame_18.bm | Bin .../NSFW/Anims}/lvl_15/frame_19.bm | Bin .../NSFW/Anims}/lvl_15/frame_2.bm | Bin .../NSFW/Anims}/lvl_15/frame_20.bm | Bin .../NSFW/Anims}/lvl_15/frame_21.bm | Bin .../NSFW/Anims}/lvl_15/frame_3.bm | Bin .../NSFW/Anims}/lvl_15/frame_4.bm | Bin .../NSFW/Anims}/lvl_15/frame_5.bm | Bin .../NSFW/Anims}/lvl_15/frame_6.bm | Bin .../NSFW/Anims}/lvl_15/frame_7.bm | Bin .../NSFW/Anims}/lvl_15/frame_8.bm | Bin .../NSFW/Anims}/lvl_15/frame_9.bm | Bin .../NSFW/Anims}/lvl_15/meta.txt | 0 .../NSFW/Anims}/lvl_16/frame_0.bm | Bin .../NSFW/Anims}/lvl_16/frame_1.bm | Bin .../NSFW/Anims}/lvl_16/frame_10.bm | Bin .../NSFW/Anims}/lvl_16/frame_11.bm | Bin .../NSFW/Anims}/lvl_16/frame_12.bm | Bin .../NSFW/Anims}/lvl_16/frame_13.bm | Bin .../NSFW/Anims}/lvl_16/frame_14.bm | Bin .../NSFW/Anims}/lvl_16/frame_15.bm | Bin .../NSFW/Anims}/lvl_16/frame_16.bm | Bin .../NSFW/Anims}/lvl_16/frame_17.bm | Bin .../NSFW/Anims}/lvl_16/frame_18.bm | Bin .../NSFW/Anims}/lvl_16/frame_19.bm | Bin .../NSFW/Anims}/lvl_16/frame_2.bm | Bin .../NSFW/Anims}/lvl_16/frame_20.bm | Bin .../NSFW/Anims}/lvl_16/frame_3.bm | Bin .../NSFW/Anims}/lvl_16/frame_4.bm | Bin .../NSFW/Anims}/lvl_16/frame_5.bm | Bin .../NSFW/Anims}/lvl_16/frame_6.bm | Bin .../NSFW/Anims}/lvl_16/frame_7.bm | Bin .../NSFW/Anims}/lvl_16/frame_8.bm | Bin .../NSFW/Anims}/lvl_16/frame_9.bm | Bin .../NSFW/Anims}/lvl_16/meta.txt | 0 .../NSFW/Anims}/lvl_17/frame_0.bm | Bin .../NSFW/Anims}/lvl_17/frame_1.bm | Bin .../NSFW/Anims}/lvl_17/frame_10.bm | Bin .../NSFW/Anims}/lvl_17/frame_11.bm | Bin .../NSFW/Anims}/lvl_17/frame_12.bm | Bin .../NSFW/Anims}/lvl_17/frame_13.bm | Bin .../NSFW/Anims}/lvl_17/frame_14.bm | Bin .../NSFW/Anims}/lvl_17/frame_15.bm | Bin .../NSFW/Anims}/lvl_17/frame_16.bm | Bin .../NSFW/Anims}/lvl_17/frame_17.bm | Bin .../NSFW/Anims}/lvl_17/frame_18.bm | Bin .../NSFW/Anims}/lvl_17/frame_19.bm | Bin .../NSFW/Anims}/lvl_17/frame_2.bm | Bin .../NSFW/Anims}/lvl_17/frame_20.bm | Bin .../NSFW/Anims}/lvl_17/frame_21.bm | Bin .../NSFW/Anims}/lvl_17/frame_22.bm | Bin .../NSFW/Anims}/lvl_17/frame_23.bm | Bin .../NSFW/Anims}/lvl_17/frame_24.bm | Bin .../NSFW/Anims}/lvl_17/frame_25.bm | Bin .../NSFW/Anims}/lvl_17/frame_26.bm | Bin .../NSFW/Anims}/lvl_17/frame_27.bm | Bin .../NSFW/Anims}/lvl_17/frame_28.bm | Bin .../NSFW/Anims}/lvl_17/frame_29.bm | Bin .../NSFW/Anims}/lvl_17/frame_3.bm | Bin .../NSFW/Anims}/lvl_17/frame_30.bm | Bin .../NSFW/Anims}/lvl_17/frame_31.bm | Bin .../NSFW/Anims}/lvl_17/frame_4.bm | Bin .../NSFW/Anims}/lvl_17/frame_5.bm | Bin .../NSFW/Anims}/lvl_17/frame_6.bm | Bin .../NSFW/Anims}/lvl_17/frame_7.bm | Bin .../NSFW/Anims}/lvl_17/frame_8.bm | Bin .../NSFW/Anims}/lvl_17/frame_9.bm | Bin .../NSFW/Anims}/lvl_17/meta.txt | 0 .../NSFW/Anims}/lvl_18/frame_0.bm | Bin .../NSFW/Anims}/lvl_18/frame_1.bm | Bin .../NSFW/Anims}/lvl_18/frame_10.bm | Bin .../NSFW/Anims}/lvl_18/frame_11.bm | Bin .../NSFW/Anims}/lvl_18/frame_12.bm | Bin .../NSFW/Anims}/lvl_18/frame_13.bm | Bin .../NSFW/Anims}/lvl_18/frame_14.bm | Bin .../NSFW/Anims}/lvl_18/frame_15.bm | Bin .../NSFW/Anims}/lvl_18/frame_16.bm | Bin .../NSFW/Anims}/lvl_18/frame_17.bm | Bin .../NSFW/Anims}/lvl_18/frame_18.bm | Bin .../NSFW/Anims}/lvl_18/frame_19.bm | Bin .../NSFW/Anims}/lvl_18/frame_2.bm | Bin .../NSFW/Anims}/lvl_18/frame_20.bm | Bin .../NSFW/Anims}/lvl_18/frame_21.bm | Bin .../NSFW/Anims}/lvl_18/frame_22.bm | Bin .../NSFW/Anims}/lvl_18/frame_3.bm | Bin .../NSFW/Anims}/lvl_18/frame_4.bm | Bin .../NSFW/Anims}/lvl_18/frame_5.bm | Bin .../NSFW/Anims}/lvl_18/frame_6.bm | Bin .../NSFW/Anims}/lvl_18/frame_7.bm | Bin .../NSFW/Anims}/lvl_18/frame_8.bm | Bin .../NSFW/Anims}/lvl_18/frame_9.bm | Bin .../NSFW/Anims}/lvl_18/meta.txt | 0 .../NSFW/Anims}/lvl_19/frame_0.bm | Bin .../NSFW/Anims}/lvl_19/frame_1.bm | Bin .../NSFW/Anims}/lvl_19/frame_10.bm | Bin .../NSFW/Anims}/lvl_19/frame_11.bm | Bin .../NSFW/Anims}/lvl_19/frame_12.bm | Bin .../NSFW/Anims}/lvl_19/frame_13.bm | Bin .../NSFW/Anims}/lvl_19/frame_14.bm | Bin .../NSFW/Anims}/lvl_19/frame_15.bm | Bin .../NSFW/Anims}/lvl_19/frame_16.bm | Bin .../NSFW/Anims}/lvl_19/frame_17.bm | Bin .../NSFW/Anims}/lvl_19/frame_18.bm | Bin .../NSFW/Anims}/lvl_19/frame_19.bm | Bin .../NSFW/Anims}/lvl_19/frame_2.bm | Bin .../NSFW/Anims}/lvl_19/frame_20.bm | Bin .../NSFW/Anims}/lvl_19/frame_21.bm | Bin .../NSFW/Anims}/lvl_19/frame_3.bm | Bin .../NSFW/Anims}/lvl_19/frame_4.bm | Bin .../NSFW/Anims}/lvl_19/frame_5.bm | Bin .../NSFW/Anims}/lvl_19/frame_6.bm | Bin .../NSFW/Anims}/lvl_19/frame_7.bm | Bin .../NSFW/Anims}/lvl_19/frame_8.bm | Bin .../NSFW/Anims}/lvl_19/frame_9.bm | Bin .../NSFW/Anims}/lvl_19/meta.txt | 0 .../NSFW/Anims}/lvl_2/frame_0.bm | Bin .../NSFW/Anims}/lvl_2/frame_1.bm | Bin .../NSFW/Anims}/lvl_2/frame_10.bm | Bin .../NSFW/Anims}/lvl_2/frame_11.bm | Bin .../NSFW/Anims}/lvl_2/frame_12.bm | Bin .../NSFW/Anims}/lvl_2/frame_13.bm | Bin .../NSFW/Anims}/lvl_2/frame_14.bm | Bin .../NSFW/Anims}/lvl_2/frame_2.bm | Bin .../NSFW/Anims}/lvl_2/frame_3.bm | Bin .../NSFW/Anims}/lvl_2/frame_4.bm | Bin .../NSFW/Anims}/lvl_2/frame_5.bm | Bin .../NSFW/Anims}/lvl_2/frame_6.bm | Bin .../NSFW/Anims}/lvl_2/frame_7.bm | Bin .../NSFW/Anims}/lvl_2/frame_8.bm | Bin .../NSFW/Anims}/lvl_2/frame_9.bm | Bin .../NSFW/Anims}/lvl_2/meta.txt | 0 .../NSFW/Anims}/lvl_20/frame_0.bm | Bin .../NSFW/Anims}/lvl_20/frame_1.bm | Bin .../NSFW/Anims}/lvl_20/frame_10.bm | Bin .../NSFW/Anims}/lvl_20/frame_11.bm | Bin .../NSFW/Anims}/lvl_20/frame_12.bm | Bin .../NSFW/Anims}/lvl_20/frame_13.bm | Bin .../NSFW/Anims}/lvl_20/frame_2.bm | Bin .../NSFW/Anims}/lvl_20/frame_3.bm | Bin .../NSFW/Anims}/lvl_20/frame_4.bm | Bin .../NSFW/Anims}/lvl_20/frame_5.bm | Bin .../NSFW/Anims}/lvl_20/frame_6.bm | Bin .../NSFW/Anims}/lvl_20/frame_7.bm | Bin .../NSFW/Anims}/lvl_20/frame_8.bm | Bin .../NSFW/Anims}/lvl_20/frame_9.bm | Bin .../NSFW/Anims}/lvl_20/meta.txt | 0 .../NSFW/Anims}/lvl_21/frame_0.bm | Bin .../NSFW/Anims}/lvl_21/frame_1.bm | Bin .../NSFW/Anims}/lvl_21/frame_2.bm | Bin .../NSFW/Anims}/lvl_21/frame_3.bm | Bin .../NSFW/Anims}/lvl_21/frame_4.bm | Bin .../NSFW/Anims}/lvl_21/frame_5.bm | Bin .../NSFW/Anims}/lvl_21/meta.txt | 0 .../NSFW/Anims}/lvl_22/frame_0.bm | Bin .../NSFW/Anims}/lvl_22/frame_1.bm | Bin .../NSFW/Anims}/lvl_22/frame_10.bm | Bin .../NSFW/Anims}/lvl_22/frame_11.bm | Bin .../NSFW/Anims}/lvl_22/frame_12.bm | Bin .../NSFW/Anims}/lvl_22/frame_13.bm | Bin .../NSFW/Anims}/lvl_22/frame_14.bm | Bin .../NSFW/Anims}/lvl_22/frame_15.bm | Bin .../NSFW/Anims}/lvl_22/frame_16.bm | Bin .../NSFW/Anims}/lvl_22/frame_17.bm | Bin .../NSFW/Anims}/lvl_22/frame_18.bm | Bin .../NSFW/Anims}/lvl_22/frame_19.bm | Bin .../NSFW/Anims}/lvl_22/frame_2.bm | Bin .../NSFW/Anims}/lvl_22/frame_20.bm | Bin .../NSFW/Anims}/lvl_22/frame_21.bm | Bin .../NSFW/Anims}/lvl_22/frame_22.bm | Bin .../NSFW/Anims}/lvl_22/frame_23.bm | Bin .../NSFW/Anims}/lvl_22/frame_24.bm | Bin .../NSFW/Anims}/lvl_22/frame_25.bm | Bin .../NSFW/Anims}/lvl_22/frame_26.bm | Bin .../NSFW/Anims}/lvl_22/frame_27.bm | Bin .../NSFW/Anims}/lvl_22/frame_28.bm | Bin .../NSFW/Anims}/lvl_22/frame_29.bm | Bin .../NSFW/Anims}/lvl_22/frame_3.bm | Bin .../NSFW/Anims}/lvl_22/frame_30.bm | Bin .../NSFW/Anims}/lvl_22/frame_31.bm | Bin .../NSFW/Anims}/lvl_22/frame_32.bm | Bin .../NSFW/Anims}/lvl_22/frame_33.bm | Bin .../NSFW/Anims}/lvl_22/frame_34.bm | Bin .../NSFW/Anims}/lvl_22/frame_35.bm | Bin .../NSFW/Anims}/lvl_22/frame_36.bm | Bin .../NSFW/Anims}/lvl_22/frame_37.bm | Bin .../NSFW/Anims}/lvl_22/frame_38.bm | Bin .../NSFW/Anims}/lvl_22/frame_39.bm | Bin .../NSFW/Anims}/lvl_22/frame_4.bm | Bin .../NSFW/Anims}/lvl_22/frame_40.bm | Bin .../NSFW/Anims}/lvl_22/frame_41.bm | Bin .../NSFW/Anims}/lvl_22/frame_42.bm | Bin .../NSFW/Anims}/lvl_22/frame_43.bm | Bin .../NSFW/Anims}/lvl_22/frame_44.bm | Bin .../NSFW/Anims}/lvl_22/frame_45.bm | Bin .../NSFW/Anims}/lvl_22/frame_46.bm | Bin .../NSFW/Anims}/lvl_22/frame_47.bm | Bin .../NSFW/Anims}/lvl_22/frame_48.bm | Bin .../NSFW/Anims}/lvl_22/frame_49.bm | Bin .../NSFW/Anims}/lvl_22/frame_5.bm | Bin .../NSFW/Anims}/lvl_22/frame_50.bm | Bin .../NSFW/Anims}/lvl_22/frame_51.bm | Bin .../NSFW/Anims}/lvl_22/frame_52.bm | Bin .../NSFW/Anims}/lvl_22/frame_53.bm | Bin .../NSFW/Anims}/lvl_22/frame_54.bm | Bin .../NSFW/Anims}/lvl_22/frame_55.bm | Bin .../NSFW/Anims}/lvl_22/frame_56.bm | Bin .../NSFW/Anims}/lvl_22/frame_57.bm | Bin .../NSFW/Anims}/lvl_22/frame_58.bm | Bin .../NSFW/Anims}/lvl_22/frame_59.bm | Bin .../NSFW/Anims}/lvl_22/frame_6.bm | Bin .../NSFW/Anims}/lvl_22/frame_7.bm | Bin .../NSFW/Anims}/lvl_22/frame_8.bm | Bin .../NSFW/Anims}/lvl_22/frame_9.bm | Bin .../NSFW/Anims}/lvl_22/meta.txt | 0 .../NSFW/Anims}/lvl_23/frame_0.bm | Bin .../NSFW/Anims}/lvl_23/frame_1.bm | Bin .../NSFW/Anims}/lvl_23/frame_10.bm | Bin .../NSFW/Anims}/lvl_23/frame_11.bm | Bin .../NSFW/Anims}/lvl_23/frame_12.bm | Bin .../NSFW/Anims}/lvl_23/frame_13.bm | Bin .../NSFW/Anims}/lvl_23/frame_14.bm | Bin .../NSFW/Anims}/lvl_23/frame_15.bm | Bin .../NSFW/Anims}/lvl_23/frame_16.bm | Bin .../NSFW/Anims}/lvl_23/frame_2.bm | Bin .../NSFW/Anims}/lvl_23/frame_3.bm | Bin .../NSFW/Anims}/lvl_23/frame_4.bm | Bin .../NSFW/Anims}/lvl_23/frame_5.bm | Bin .../NSFW/Anims}/lvl_23/frame_6.bm | Bin .../NSFW/Anims}/lvl_23/frame_7.bm | Bin .../NSFW/Anims}/lvl_23/frame_8.bm | Bin .../NSFW/Anims}/lvl_23/frame_9.bm | Bin .../NSFW/Anims}/lvl_23/meta.txt | 0 .../NSFW/Anims}/lvl_24/frame_0.bm | Bin .../NSFW/Anims}/lvl_24/frame_1.bm | Bin .../NSFW/Anims}/lvl_24/frame_10.bm | Bin .../NSFW/Anims}/lvl_24/frame_11.bm | Bin .../NSFW/Anims}/lvl_24/frame_12.bm | Bin .../NSFW/Anims}/lvl_24/frame_13.bm | Bin .../NSFW/Anims}/lvl_24/frame_14.bm | Bin .../NSFW/Anims}/lvl_24/frame_15.bm | Bin .../NSFW/Anims}/lvl_24/frame_16.bm | Bin .../NSFW/Anims}/lvl_24/frame_17.bm | Bin .../NSFW/Anims}/lvl_24/frame_18.bm | Bin .../NSFW/Anims}/lvl_24/frame_19.bm | Bin .../NSFW/Anims}/lvl_24/frame_2.bm | Bin .../NSFW/Anims}/lvl_24/frame_20.bm | Bin .../NSFW/Anims}/lvl_24/frame_21.bm | Bin .../NSFW/Anims}/lvl_24/frame_22.bm | Bin .../NSFW/Anims}/lvl_24/frame_23.bm | Bin .../NSFW/Anims}/lvl_24/frame_24.bm | Bin .../NSFW/Anims}/lvl_24/frame_25.bm | Bin .../NSFW/Anims}/lvl_24/frame_26.bm | Bin .../NSFW/Anims}/lvl_24/frame_27.bm | Bin .../NSFW/Anims}/lvl_24/frame_28.bm | Bin .../NSFW/Anims}/lvl_24/frame_29.bm | Bin .../NSFW/Anims}/lvl_24/frame_3.bm | Bin .../NSFW/Anims}/lvl_24/frame_4.bm | Bin .../NSFW/Anims}/lvl_24/frame_5.bm | Bin .../NSFW/Anims}/lvl_24/frame_6.bm | Bin .../NSFW/Anims}/lvl_24/frame_7.bm | Bin .../NSFW/Anims}/lvl_24/frame_8.bm | Bin .../NSFW/Anims}/lvl_24/frame_9.bm | Bin .../NSFW/Anims}/lvl_24/meta.txt | 0 .../NSFW/Anims}/lvl_25/frame_0.bm | Bin .../NSFW/Anims}/lvl_25/frame_1.bm | Bin .../NSFW/Anims}/lvl_25/frame_10.bm | Bin .../NSFW/Anims}/lvl_25/frame_11.bm | Bin .../NSFW/Anims}/lvl_25/frame_12.bm | Bin .../NSFW/Anims}/lvl_25/frame_13.bm | Bin .../NSFW/Anims}/lvl_25/frame_14.bm | Bin .../NSFW/Anims}/lvl_25/frame_15.bm | Bin .../NSFW/Anims}/lvl_25/frame_16.bm | Bin .../NSFW/Anims}/lvl_25/frame_17.bm | Bin .../NSFW/Anims}/lvl_25/frame_18.bm | Bin .../NSFW/Anims}/lvl_25/frame_19.bm | Bin .../NSFW/Anims}/lvl_25/frame_2.bm | Bin .../NSFW/Anims}/lvl_25/frame_20.bm | Bin .../NSFW/Anims}/lvl_25/frame_21.bm | Bin .../NSFW/Anims}/lvl_25/frame_22.bm | Bin .../NSFW/Anims}/lvl_25/frame_23.bm | Bin .../NSFW/Anims}/lvl_25/frame_24.bm | Bin .../NSFW/Anims}/lvl_25/frame_25.bm | Bin .../NSFW/Anims}/lvl_25/frame_26.bm | Bin .../NSFW/Anims}/lvl_25/frame_27.bm | Bin .../NSFW/Anims}/lvl_25/frame_28.bm | Bin .../NSFW/Anims}/lvl_25/frame_29.bm | Bin .../NSFW/Anims}/lvl_25/frame_3.bm | Bin .../NSFW/Anims}/lvl_25/frame_30.bm | Bin .../NSFW/Anims}/lvl_25/frame_31.bm | Bin .../NSFW/Anims}/lvl_25/frame_32.bm | Bin .../NSFW/Anims}/lvl_25/frame_33.bm | Bin .../NSFW/Anims}/lvl_25/frame_34.bm | Bin .../NSFW/Anims}/lvl_25/frame_35.bm | Bin .../NSFW/Anims}/lvl_25/frame_4.bm | Bin .../NSFW/Anims}/lvl_25/frame_5.bm | Bin .../NSFW/Anims}/lvl_25/frame_6.bm | Bin .../NSFW/Anims}/lvl_25/frame_7.bm | Bin .../NSFW/Anims}/lvl_25/frame_8.bm | Bin .../NSFW/Anims}/lvl_25/frame_9.bm | Bin .../NSFW/Anims}/lvl_25/meta.txt | 0 .../NSFW/Anims}/lvl_26/frame_0.bm | Bin .../NSFW/Anims}/lvl_26/frame_1.bm | Bin .../NSFW/Anims}/lvl_26/frame_10.bm | Bin .../NSFW/Anims}/lvl_26/frame_11.bm | Bin .../NSFW/Anims}/lvl_26/frame_2.bm | Bin .../NSFW/Anims}/lvl_26/frame_3.bm | Bin .../NSFW/Anims}/lvl_26/frame_4.bm | Bin .../NSFW/Anims}/lvl_26/frame_5.bm | Bin .../NSFW/Anims}/lvl_26/frame_6.bm | Bin .../NSFW/Anims}/lvl_26/frame_7.bm | Bin .../NSFW/Anims}/lvl_26/frame_8.bm | Bin .../NSFW/Anims}/lvl_26/frame_9.bm | Bin .../NSFW/Anims}/lvl_26/meta.txt | 0 .../NSFW/Anims}/lvl_27/frame_0.bm | Bin .../NSFW/Anims}/lvl_27/frame_1.bm | Bin .../NSFW/Anims}/lvl_27/frame_10.bm | Bin .../NSFW/Anims}/lvl_27/frame_11.bm | Bin .../NSFW/Anims}/lvl_27/frame_12.bm | Bin .../NSFW/Anims}/lvl_27/frame_13.bm | Bin .../NSFW/Anims}/lvl_27/frame_14.bm | Bin .../NSFW/Anims}/lvl_27/frame_15.bm | Bin .../NSFW/Anims}/lvl_27/frame_16.bm | Bin .../NSFW/Anims}/lvl_27/frame_17.bm | Bin .../NSFW/Anims}/lvl_27/frame_18.bm | Bin .../NSFW/Anims}/lvl_27/frame_19.bm | Bin .../NSFW/Anims}/lvl_27/frame_2.bm | Bin .../NSFW/Anims}/lvl_27/frame_20.bm | Bin .../NSFW/Anims}/lvl_27/frame_21.bm | Bin .../NSFW/Anims}/lvl_27/frame_3.bm | Bin .../NSFW/Anims}/lvl_27/frame_4.bm | Bin .../NSFW/Anims}/lvl_27/frame_5.bm | Bin .../NSFW/Anims}/lvl_27/frame_6.bm | Bin .../NSFW/Anims}/lvl_27/frame_7.bm | Bin .../NSFW/Anims}/lvl_27/frame_8.bm | Bin .../NSFW/Anims}/lvl_27/frame_9.bm | Bin .../NSFW/Anims}/lvl_27/meta.txt | 0 .../NSFW/Anims}/lvl_28/frame_0.bm | Bin .../NSFW/Anims}/lvl_28/frame_1.bm | Bin .../NSFW/Anims}/lvl_28/frame_2.bm | Bin .../NSFW/Anims}/lvl_28/frame_3.bm | Bin .../NSFW/Anims}/lvl_28/frame_4.bm | Bin .../NSFW/Anims}/lvl_28/frame_5.bm | Bin .../NSFW/Anims}/lvl_28/meta.txt | 0 .../NSFW/Anims}/lvl_29/frame_0.bm | Bin .../NSFW/Anims}/lvl_29/frame_1.bm | Bin .../NSFW/Anims}/lvl_29/frame_10.bm | Bin .../NSFW/Anims}/lvl_29/frame_11.bm | Bin .../NSFW/Anims}/lvl_29/frame_12.bm | Bin .../NSFW/Anims}/lvl_29/frame_13.bm | Bin .../NSFW/Anims}/lvl_29/frame_14.bm | Bin .../NSFW/Anims}/lvl_29/frame_15.bm | Bin .../NSFW/Anims}/lvl_29/frame_16.bm | Bin .../NSFW/Anims}/lvl_29/frame_17.bm | Bin .../NSFW/Anims}/lvl_29/frame_18.bm | Bin .../NSFW/Anims}/lvl_29/frame_19.bm | Bin .../NSFW/Anims}/lvl_29/frame_2.bm | Bin .../NSFW/Anims}/lvl_29/frame_20.bm | Bin .../NSFW/Anims}/lvl_29/frame_21.bm | Bin .../NSFW/Anims}/lvl_29/frame_22.bm | Bin .../NSFW/Anims}/lvl_29/frame_23.bm | Bin .../NSFW/Anims}/lvl_29/frame_24.bm | Bin .../NSFW/Anims}/lvl_29/frame_25.bm | Bin .../NSFW/Anims}/lvl_29/frame_26.bm | Bin .../NSFW/Anims}/lvl_29/frame_27.bm | Bin .../NSFW/Anims}/lvl_29/frame_28.bm | Bin .../NSFW/Anims}/lvl_29/frame_29.bm | Bin .../NSFW/Anims}/lvl_29/frame_3.bm | Bin .../NSFW/Anims}/lvl_29/frame_30.bm | Bin .../NSFW/Anims}/lvl_29/frame_31.bm | Bin .../NSFW/Anims}/lvl_29/frame_32.bm | Bin .../NSFW/Anims}/lvl_29/frame_33.bm | Bin .../NSFW/Anims}/lvl_29/frame_34.bm | Bin .../NSFW/Anims}/lvl_29/frame_35.bm | Bin .../NSFW/Anims}/lvl_29/frame_36.bm | Bin .../NSFW/Anims}/lvl_29/frame_37.bm | Bin .../NSFW/Anims}/lvl_29/frame_38.bm | Bin .../NSFW/Anims}/lvl_29/frame_39.bm | Bin .../NSFW/Anims}/lvl_29/frame_4.bm | Bin .../NSFW/Anims}/lvl_29/frame_40.bm | Bin .../NSFW/Anims}/lvl_29/frame_41.bm | Bin .../NSFW/Anims}/lvl_29/frame_42.bm | Bin .../NSFW/Anims}/lvl_29/frame_43.bm | Bin .../NSFW/Anims}/lvl_29/frame_44.bm | Bin .../NSFW/Anims}/lvl_29/frame_45.bm | Bin .../NSFW/Anims}/lvl_29/frame_46.bm | Bin .../NSFW/Anims}/lvl_29/frame_47.bm | Bin .../NSFW/Anims}/lvl_29/frame_48.bm | Bin .../NSFW/Anims}/lvl_29/frame_49.bm | Bin .../NSFW/Anims}/lvl_29/frame_5.bm | Bin .../NSFW/Anims}/lvl_29/frame_50.bm | Bin .../NSFW/Anims}/lvl_29/frame_51.bm | Bin .../NSFW/Anims}/lvl_29/frame_6.bm | Bin .../NSFW/Anims}/lvl_29/frame_7.bm | Bin .../NSFW/Anims}/lvl_29/frame_8.bm | Bin .../NSFW/Anims}/lvl_29/frame_9.bm | Bin .../NSFW/Anims}/lvl_29/meta.txt | 0 .../NSFW/Anims}/lvl_3/frame_0.bm | Bin .../NSFW/Anims}/lvl_3/frame_1.bm | Bin .../NSFW/Anims}/lvl_3/frame_10.bm | Bin .../NSFW/Anims}/lvl_3/frame_11.bm | Bin .../NSFW/Anims}/lvl_3/frame_12.bm | Bin .../NSFW/Anims}/lvl_3/frame_13.bm | Bin .../NSFW/Anims}/lvl_3/frame_14.bm | Bin .../NSFW/Anims}/lvl_3/frame_2.bm | Bin .../NSFW/Anims}/lvl_3/frame_3.bm | Bin .../NSFW/Anims}/lvl_3/frame_4.bm | Bin .../NSFW/Anims}/lvl_3/frame_5.bm | Bin .../NSFW/Anims}/lvl_3/frame_6.bm | Bin .../NSFW/Anims}/lvl_3/frame_7.bm | Bin .../NSFW/Anims}/lvl_3/frame_8.bm | Bin .../NSFW/Anims}/lvl_3/frame_9.bm | Bin .../NSFW/Anims}/lvl_3/meta.txt | 0 .../NSFW/Anims}/lvl_30/frame_0.bm | Bin .../NSFW/Anims}/lvl_30/frame_1.bm | Bin .../NSFW/Anims}/lvl_30/frame_10.bm | Bin .../NSFW/Anims}/lvl_30/frame_11.bm | Bin .../NSFW/Anims}/lvl_30/frame_12.bm | Bin .../NSFW/Anims}/lvl_30/frame_13.bm | Bin .../NSFW/Anims}/lvl_30/frame_14.bm | Bin .../NSFW/Anims}/lvl_30/frame_15.bm | Bin .../NSFW/Anims}/lvl_30/frame_16.bm | Bin .../NSFW/Anims}/lvl_30/frame_17.bm | Bin .../NSFW/Anims}/lvl_30/frame_18.bm | Bin .../NSFW/Anims}/lvl_30/frame_19.bm | Bin .../NSFW/Anims}/lvl_30/frame_2.bm | Bin .../NSFW/Anims}/lvl_30/frame_20.bm | Bin .../NSFW/Anims}/lvl_30/frame_21.bm | Bin .../NSFW/Anims}/lvl_30/frame_22.bm | Bin .../NSFW/Anims}/lvl_30/frame_23.bm | Bin .../NSFW/Anims}/lvl_30/frame_24.bm | Bin .../NSFW/Anims}/lvl_30/frame_25.bm | Bin .../NSFW/Anims}/lvl_30/frame_26.bm | Bin .../NSFW/Anims}/lvl_30/frame_27.bm | Bin .../NSFW/Anims}/lvl_30/frame_28.bm | Bin .../NSFW/Anims}/lvl_30/frame_29.bm | Bin .../NSFW/Anims}/lvl_30/frame_3.bm | Bin .../NSFW/Anims}/lvl_30/frame_30.bm | Bin .../NSFW/Anims}/lvl_30/frame_31.bm | Bin .../NSFW/Anims}/lvl_30/frame_32.bm | Bin .../NSFW/Anims}/lvl_30/frame_33.bm | Bin .../NSFW/Anims}/lvl_30/frame_34.bm | Bin .../NSFW/Anims}/lvl_30/frame_35.bm | Bin .../NSFW/Anims}/lvl_30/frame_36.bm | Bin .../NSFW/Anims}/lvl_30/frame_37.bm | Bin .../NSFW/Anims}/lvl_30/frame_38.bm | Bin .../NSFW/Anims}/lvl_30/frame_39.bm | Bin .../NSFW/Anims}/lvl_30/frame_4.bm | Bin .../NSFW/Anims}/lvl_30/frame_40.bm | Bin .../NSFW/Anims}/lvl_30/frame_41.bm | Bin .../NSFW/Anims}/lvl_30/frame_42.bm | Bin .../NSFW/Anims}/lvl_30/frame_43.bm | Bin .../NSFW/Anims}/lvl_30/frame_44.bm | Bin .../NSFW/Anims}/lvl_30/frame_45.bm | Bin .../NSFW/Anims}/lvl_30/frame_46.bm | Bin .../NSFW/Anims}/lvl_30/frame_47.bm | Bin .../NSFW/Anims}/lvl_30/frame_48.bm | Bin .../NSFW/Anims}/lvl_30/frame_49.bm | Bin .../NSFW/Anims}/lvl_30/frame_5.bm | Bin .../NSFW/Anims}/lvl_30/frame_6.bm | Bin .../NSFW/Anims}/lvl_30/frame_7.bm | Bin .../NSFW/Anims}/lvl_30/frame_8.bm | Bin .../NSFW/Anims}/lvl_30/frame_9.bm | Bin .../NSFW/Anims}/lvl_30/meta.txt | 0 .../NSFW/Anims}/lvl_4/frame_0.bm | Bin .../NSFW/Anims}/lvl_4/frame_1.bm | Bin .../NSFW/Anims}/lvl_4/frame_10.bm | Bin .../NSFW/Anims}/lvl_4/frame_11.bm | Bin .../NSFW/Anims}/lvl_4/frame_12.bm | Bin .../NSFW/Anims}/lvl_4/frame_13.bm | Bin .../NSFW/Anims}/lvl_4/frame_14.bm | Bin .../NSFW/Anims}/lvl_4/frame_15.bm | Bin .../NSFW/Anims}/lvl_4/frame_16.bm | Bin .../NSFW/Anims}/lvl_4/frame_17.bm | Bin .../NSFW/Anims}/lvl_4/frame_18.bm | Bin .../NSFW/Anims}/lvl_4/frame_19.bm | Bin .../NSFW/Anims}/lvl_4/frame_2.bm | Bin .../NSFW/Anims}/lvl_4/frame_3.bm | Bin .../NSFW/Anims}/lvl_4/frame_4.bm | Bin .../NSFW/Anims}/lvl_4/frame_5.bm | Bin .../NSFW/Anims}/lvl_4/frame_6.bm | Bin .../NSFW/Anims}/lvl_4/frame_7.bm | Bin .../NSFW/Anims}/lvl_4/frame_8.bm | Bin .../NSFW/Anims}/lvl_4/frame_9.bm | Bin .../NSFW/Anims}/lvl_4/meta.txt | 0 .../NSFW/Anims}/lvl_5/frame_0.bm | Bin .../NSFW/Anims}/lvl_5/frame_1.bm | Bin .../NSFW/Anims}/lvl_5/frame_10.bm | Bin .../NSFW/Anims}/lvl_5/frame_11.bm | Bin .../NSFW/Anims}/lvl_5/frame_12.bm | Bin .../NSFW/Anims}/lvl_5/frame_13.bm | Bin .../NSFW/Anims}/lvl_5/frame_14.bm | Bin .../NSFW/Anims}/lvl_5/frame_15.bm | Bin .../NSFW/Anims}/lvl_5/frame_16.bm | Bin .../NSFW/Anims}/lvl_5/frame_17.bm | Bin .../NSFW/Anims}/lvl_5/frame_18.bm | Bin .../NSFW/Anims}/lvl_5/frame_19.bm | Bin .../NSFW/Anims}/lvl_5/frame_2.bm | Bin .../NSFW/Anims}/lvl_5/frame_20.bm | Bin .../NSFW/Anims}/lvl_5/frame_21.bm | Bin .../NSFW/Anims}/lvl_5/frame_22.bm | Bin .../NSFW/Anims}/lvl_5/frame_23.bm | Bin .../NSFW/Anims}/lvl_5/frame_24.bm | Bin .../NSFW/Anims}/lvl_5/frame_25.bm | Bin .../NSFW/Anims}/lvl_5/frame_26.bm | Bin .../NSFW/Anims}/lvl_5/frame_27.bm | Bin .../NSFW/Anims}/lvl_5/frame_3.bm | Bin .../NSFW/Anims}/lvl_5/frame_4.bm | Bin .../NSFW/Anims}/lvl_5/frame_5.bm | Bin .../NSFW/Anims}/lvl_5/frame_6.bm | Bin .../NSFW/Anims}/lvl_5/frame_7.bm | Bin .../NSFW/Anims}/lvl_5/frame_8.bm | Bin .../NSFW/Anims}/lvl_5/frame_9.bm | Bin .../NSFW/Anims}/lvl_5/meta.txt | 0 .../NSFW/Anims}/lvl_6/frame_0.bm | Bin .../NSFW/Anims}/lvl_6/frame_1.bm | Bin .../NSFW/Anims}/lvl_6/frame_2.bm | Bin .../NSFW/Anims}/lvl_6/frame_3.bm | Bin .../NSFW/Anims}/lvl_6/frame_4.bm | Bin .../NSFW/Anims}/lvl_6/frame_5.bm | Bin .../NSFW/Anims}/lvl_6/frame_6.bm | Bin .../NSFW/Anims}/lvl_6/meta.txt | 0 .../NSFW/Anims}/lvl_7/frame_0.bm | Bin .../NSFW/Anims}/lvl_7/frame_1.bm | Bin .../NSFW/Anims}/lvl_7/frame_10.bm | Bin .../NSFW/Anims}/lvl_7/frame_11.bm | Bin .../NSFW/Anims}/lvl_7/frame_12.bm | Bin .../NSFW/Anims}/lvl_7/frame_13.bm | Bin .../NSFW/Anims}/lvl_7/frame_2.bm | Bin .../NSFW/Anims}/lvl_7/frame_3.bm | Bin .../NSFW/Anims}/lvl_7/frame_4.bm | Bin .../NSFW/Anims}/lvl_7/frame_5.bm | Bin .../NSFW/Anims}/lvl_7/frame_6.bm | Bin .../NSFW/Anims}/lvl_7/frame_7.bm | Bin .../NSFW/Anims}/lvl_7/frame_8.bm | Bin .../NSFW/Anims}/lvl_7/frame_9.bm | Bin .../NSFW/Anims}/lvl_7/meta.txt | 2 +- .../NSFW/Anims}/lvl_8/frame_0.bm | Bin .../NSFW/Anims}/lvl_8/frame_1.bm | Bin .../NSFW/Anims}/lvl_8/frame_2.bm | Bin .../NSFW/Anims}/lvl_8/frame_3.bm | Bin .../NSFW/Anims}/lvl_8/frame_4.bm | Bin .../NSFW/Anims}/lvl_8/frame_5.bm | Bin .../NSFW/Anims}/lvl_8/meta.txt | 0 .../NSFW/Anims}/lvl_9/frame_0.bm | Bin .../NSFW/Anims}/lvl_9/frame_1.bm | Bin .../NSFW/Anims}/lvl_9/frame_2.bm | Bin .../NSFW/Anims}/lvl_9/frame_3.bm | Bin .../NSFW/Anims}/lvl_9/frame_4.bm | Bin .../NSFW/Anims}/lvl_9/frame_5.bm | Bin .../NSFW/Anims}/lvl_9/frame_6.bm | Bin .../NSFW/Anims}/lvl_9/frame_7.bm | Bin .../NSFW/Anims}/lvl_9/meta.txt | 0 .../NSFW/Anims}/manifest.txt | 0 .../Animations/Levelup_128x64/frame_00.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_01.bm | Bin 0 -> 217 bytes .../Animations/Levelup_128x64/frame_02.bm | Bin 0 -> 219 bytes .../Animations/Levelup_128x64/frame_03.bm | Bin 0 -> 224 bytes .../Animations/Levelup_128x64/frame_04.bm | Bin 0 -> 221 bytes .../Animations/Levelup_128x64/frame_05.bm | Bin 0 -> 223 bytes .../Animations/Levelup_128x64/frame_06.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_07.bm | Bin 0 -> 227 bytes .../Animations/Levelup_128x64/frame_08.bm | Bin 0 -> 228 bytes .../Animations/Levelup_128x64/frame_09.bm | Bin 0 -> 222 bytes .../Animations/Levelup_128x64/frame_10.bm | Bin 0 -> 224 bytes .../Animations/Levelup_128x64/frame_11.bm | Bin 0 -> 221 bytes .../Animations/Levelup_128x64/frame_12.bm | Bin 0 -> 223 bytes .../Animations/Levelup_128x64/frame_13.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_14.bm | Bin 0 -> 227 bytes .../Animations/Levelup_128x64/frame_15.bm | Bin 0 -> 228 bytes .../Animations/Levelup_128x64/frame_16.bm | Bin 0 -> 222 bytes .../Animations/Levelup_128x64/frame_17.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_18.bm | Bin 0 -> 217 bytes .../Animations/Levelup_128x64/frame_19.bm | Bin 0 -> 219 bytes .../Animations/Levelup_128x64/frame_20.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_21.bm | Bin 0 -> 217 bytes .../Animations/Levelup_128x64/frame_22.bm | Bin 0 -> 219 bytes .../Animations/Levelup_128x64/frame_23.bm | Bin 0 -> 224 bytes .../Animations/Levelup_128x64/frame_24.bm | Bin 0 -> 221 bytes .../Animations/Levelup_128x64/frame_25.bm | Bin 0 -> 223 bytes .../Animations/Levelup_128x64/frame_26.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_27.bm | Bin 0 -> 227 bytes .../Animations/Levelup_128x64/frame_28.bm | Bin 0 -> 228 bytes .../Animations/Levelup_128x64/frame_29.bm | Bin 0 -> 222 bytes .../Animations/Levelup_128x64/frame_30.bm | Bin 0 -> 224 bytes .../Animations/Levelup_128x64/frame_31.bm | Bin 0 -> 518 bytes .../NSFW/Icons/Animations/Levelup_128x64/meta | Bin 0 -> 16 bytes .../NSFW/Icons/BLE/BLE_Pairing_128x64.bmx | Bin 0 -> 480 bytes .../Icons/Dolphin/DolphinCommon_56x48.bmx | Bin 0 -> 325 bytes .../Infrared/DolphinReadingSuccess_59x63.bmx | Bin 0 -> 513 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.bmx | Bin 0 -> 375 bytes .../NSFW/Icons/Passport/passport_DB.bmx | Bin 0 -> 286 bytes .../Icons/Passport/passport_bad_46x49.bmx | Bin 0 -> 297 bytes .../Icons/Passport/passport_happy_46x49.bmx | Bin 0 -> 297 bytes .../Icons/Passport/passport_okay_46x49.bmx | Bin 0 -> 297 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.bmx | Bin 0 -> 525 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx | Bin 0 -> 525 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.bmx | Bin 0 -> 502 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.bmx | Bin 0 -> 352 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.bmx | Bin 0 -> 458 bytes .../NSFW/Icons/U2F/Auth_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Connect_me_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Connected_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Error_62x31.bmx | Bin 0 -> 257 bytes .../Icons/iButton/DolphinMafia_115x62.bmx | Bin 0 -> 684 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.bmx | Bin 0 -> 609 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.bmx | Bin 0 -> 481 bytes .../iButtonDolphinVerySuccess_108x52.bmx | Bin 0 -> 482 bytes scripts/asset_packer.py | 70 +++-- scripts/assets.py | 35 +-- 2348 files changed, 216 insertions(+), 298 deletions(-) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_50.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_51.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_52.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_53.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_54.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_55.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_56.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_57.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_58.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_59.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_50.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_51.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/manifest.txt (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_00.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_01.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_02.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_03.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_04.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_05.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_06.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_07.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_08.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_09.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_10.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_11.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_12.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_13.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_14.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_15.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_16.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_17.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_18.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_19.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_20.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_21.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_22.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_23.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_24.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_25.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_26.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_27.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_28.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_29.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_30.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_31.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_rate (100%) create mode 100644 assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.png create mode 100644 assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_DB.png rename assets/{icons/Passport/flipper.png => dolphin/custom/NSFW/Icons/Passport/passport_bad_46x49.png} (100%) create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_happy_46x49.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png create mode 100644 assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Auth_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connect_me_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Error_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinMafia_115x62.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinNice_96x59.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_21.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_22.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_23.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_24.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_25.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_26.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_27.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_28.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_29.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_30.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_31.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_32.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_33.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_34.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_35.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_36.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_37.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_38.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_39.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_40.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_21.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_22.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_23.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_24.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_25.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_26.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_27.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_28.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_29.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_30.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_31.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_32.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_33.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_34.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_35.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_36.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/meta.txt (100%) rename assets/dolphin/external/{sfw => }/manifest.txt (100%) delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_03.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_06.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_10.png delete mode 100644 assets/icons/Animations/Levelup2_128x64_sfw/frame_rate rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_00.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_01.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_02.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_03.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_04.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_05.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_06.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_07.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_08.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_09.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_10.png (100%) rename assets/icons/Animations/{Levelup1_128x64_sfw => Levelup_128x64}/frame_rate (100%) delete mode 100644 assets/icons/BLE/BLE_Pairing_128x64_sfw.png delete mode 100644 assets/icons/Dolphin/DolphinCommon_56x48_sfw.png delete mode 100644 assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png delete mode 100644 assets/icons/Interface/SmallArrowDown_4x7.png delete mode 100644 assets/icons/Interface/SmallArrowUp_4x7.png delete mode 100644 assets/icons/NFC/ArrowC_1_36x36.png delete mode 100644 assets/icons/NFC/Detailed_chip_17x13.png delete mode 100644 assets/icons/NFC/Medium-chip-22x21.png delete mode 100644 assets/icons/NFC/NFC_dolphin_emulation_47x61_sfw.png delete mode 100644 assets/icons/NFC/Tap_reader_36x38.png delete mode 100644 assets/icons/Passport/passport_DB_sfw.png delete mode 100644 assets/icons/Passport/passport_bad1_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_bad3_46x49_sfw.png rename assets/icons/Passport/{passport_bad2_46x49_sfw.png => passport_bad_46x49.png} (100%) delete mode 100644 assets/icons/Passport/passport_happy1_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_happy3_46x49_sfw.png rename assets/icons/Passport/{passport_happy2_46x49_sfw.png => passport_happy_46x49.png} (100%) delete mode 100644 assets/icons/Passport/passport_okay1_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_okay3_46x49_sfw.png rename assets/icons/Passport/{passport_okay2_46x49_sfw.png => passport_okay_46x49.png} (100%) delete mode 100644 assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png delete mode 100644 assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png delete mode 100644 assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png delete mode 100644 assets/icons/Settings/Cry_dolph_55x52_sfw.png delete mode 100644 assets/icons/StatusBar/Alert_9x8.png delete mode 100644 assets/icons/StatusBar/Attention_5x8.png rename assets/icons/StatusBar/{Charging-lightning_9x10.png => Charging_lightning_9x10.png} (100%) rename assets/icons/StatusBar/{Charging-lightning_mask_9x10.png => Charging_lightning_mask_9x10.png} (100%) delete mode 100644 assets/icons/StatusBar/GameMode_11x8.png delete mode 100644 assets/icons/SubGhz/Scanning_123x52_sfw.png delete mode 100644 assets/icons/U2F/Auth_62x31_sfw.png delete mode 100644 assets/icons/U2F/Connect_me_62x31_sfw.png delete mode 100644 assets/icons/U2F/Connected_62x31_sfw.png delete mode 100644 assets/icons/U2F/Error_62x31_sfw.png delete mode 100644 assets/icons/iButton/DolphinMafia_115x62_sfw.png delete mode 100644 assets/icons/iButton/DolphinNice_96x59_sfw.png delete mode 100644 assets/icons/iButton/DolphinWait_61x59_sfw.png delete mode 100644 assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_37.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_38.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_39.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_40.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/manifest.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_21.bm (100%) create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/meta.txt (93%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_50.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_51.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_52.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_53.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_54.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_55.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_56.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_57.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_58.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_59.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_50.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_51.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/meta.txt (92%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/manifest.txt (100%) create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_bad_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinNice_96x59.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinWait_61x59.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx diff --git a/.gitignore b/.gitignore index bf7addba9..025246faa 100644 --- a/.gitignore +++ b/.gitignore @@ -74,7 +74,9 @@ lib/STM32CubeWB # Asset packs assets/dolphin/custom/* +!assets/dolphin/custom/NSFW/ !assets/dolphin/custom/WatchDogs/ !assets/dolphin/custom/ReadMe.md assets/resources/dolphin_custom/* +!assets/resources/dolphin_custom/NSFW/ !assets/resources/dolphin_custom/WatchDogs/ diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 3163b8cff..9cb66a5e5 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -457,10 +457,14 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { browser->last_tab_switch_dir = key; - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; + for(int i = 0; i < 2; i++) { + if(key == InputKeyLeft) { + tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; + } else { + tab = (tab + 1) % ArchiveTabTotal; + } + if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) continue; + break; } browser->is_root = true; diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 5b13e98da..43a9a651a 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -17,6 +17,7 @@ static const char* tab_default_paths[] = { [ArchiveTabBadUsb] = ANY_PATH("badusb"), [ArchiveTabU2f] = "/app:u2f", [ArchiveTabApplications] = ANY_PATH("apps"), + [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX, [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, }; @@ -44,6 +45,7 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, [ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabApplications] = ArchiveFileTypeApplication, + [ArchiveTabInternal] = ArchiveFileTypeUnknown, [ArchiveTabBrowser] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index dce753fde..26ed17d75 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -19,6 +19,7 @@ static const char* ArchiveTabNames[] = { [ArchiveTabBadUsb] = "Bad USB", [ArchiveTabU2f] = "U2F", [ArchiveTabApplications] = "Apps", + [ArchiveTabInternal] = "Internal", [ArchiveTabBrowser] = "Browser", }; diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index a525a7db6..6e6582405 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -31,6 +31,7 @@ typedef enum { ArchiveTabBadUsb, ArchiveTabU2f, ArchiveTabApplications, + ArchiveTabInternal, ArchiveTabBrowser, ArchiveTabTotal, } ArchiveTabEnum; diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_usb/scenes/bad_usb_scene_error.c index 2c707bbf1..d32eee382 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_error.c @@ -1,5 +1,5 @@ #include "../bad_usb_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" typedef enum { BadUsbCustomEventErrorBack, @@ -32,7 +32,7 @@ void bad_usb_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); } else if(app->error == BadUsbAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index ad889cd1c..51bed3835 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -3,7 +3,7 @@ #include #include #include -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define MAX_NAME_LEN 64 @@ -28,7 +28,6 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); if(strlen(model->layout) == 0) { furi_string_set(disp_str, "(default)"); @@ -49,7 +48,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || (model->state.state == BadUsbStateNotConnected)) { - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "Cum"); } else { elements_button_center(canvas, "Start"); @@ -68,7 +67,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if(model->state.state == BadUsbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy"); } else { @@ -78,7 +77,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { } else if(model->state.state == BadUsbStateWillRun) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum"); } else { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index 162faf2f1..35a6ce1d9 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -1,5 +1,5 @@ #include "../u2f_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); @@ -27,7 +27,7 @@ void u2f_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); } else if(app->error == U2fAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index eecd32702..fc1c5c4fa 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -21,7 +21,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { if(model->display_msg == U2fMsgNotConnected) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connect_me_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned( canvas, 128 / 2, 3, AlignCenter, AlignTop, "Plug me in d-daddy"); } else { @@ -32,7 +32,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connected!"); } else if(model->display_msg == U2fMsgRegister) { - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -44,7 +44,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to register"); } } else if(model->display_msg == U2fMsgAuth) { - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -57,7 +57,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgSuccess) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Cum released~"); } else { canvas_draw_str_aligned( @@ -65,7 +65,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgError) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Error_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Unable to cum"); } else { canvas_draw_str_aligned( diff --git a/applications/plugins/nightstand/application.fam b/applications/plugins/nightstand/application.fam index caec3cbf6..cea02bd00 100644 --- a/applications/plugins/nightstand/application.fam +++ b/applications/plugins/nightstand/application.fam @@ -10,4 +10,3 @@ App( fap_category="Misc", order=81, ) - diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam index 27e73a0ce..750373342 100644 --- a/applications/plugins/pomodoro/application.fam +++ b/applications/plugins/pomodoro/application.fam @@ -9,4 +9,4 @@ App( fap_icon_assets="images", fap_icon="flipp_pomodoro_10.png", fap_icon_assets_symbol="flipp_pomodoro", -) \ No newline at end of file +) diff --git a/applications/plugins/scrambler/application.fam b/applications/plugins/scrambler/application.fam index 8d87a4a62..4d48d7bb5 100644 --- a/applications/plugins/scrambler/application.fam +++ b/applications/plugins/scrambler/application.fam @@ -1,6 +1,6 @@ # COMPILE ISTRUCTIONS: -# Clean the code and remove old binaries/compilation artefact +# Clean the code and remove old binaries/compilation artefact # ./fbt -c fap_rubiks_cube_scrambler # Compile FAP diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 5239d72d5..0808a3618 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -13,7 +13,7 @@ #include "animation_storage.h" #include "animation_manager.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define TAG "AnimationManager" @@ -569,9 +569,6 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m static void animation_manager_switch_to_one_shot_view(AnimationManager* animation_manager) { furi_assert(animation_manager); furi_assert(!animation_manager->one_shot_view); - Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); - DolphinStats stats = dolphin_stats(dolphin); - furi_record_close(RECORD_DOLPHIN); animation_manager->one_shot_view = one_shot_view_alloc(); one_shot_view_set_interact_callback( @@ -580,19 +577,8 @@ static void animation_manager_switch_to_one_shot_view(AnimationManager* animatio View* next_view = one_shot_view_get_view(animation_manager->one_shot_view); view_stack_remove_view(animation_manager->view_stack, prev_view); view_stack_add_view(animation_manager->view_stack, next_view); - if(XTREME_SETTINGS()->nsfw_mode) { - one_shot_view_start_animation(animation_manager->one_shot_view, &A_Levelup1_128x64); - } else { - if(stats.level <= 20) { - one_shot_view_start_animation( - animation_manager->one_shot_view, &A_Levelup1_128x64_sfw); - } else if(stats.level >= 21) { - one_shot_view_start_animation( - animation_manager->one_shot_view, &A_Levelup2_128x64_sfw); - } else { - furi_assert(0); - } - } + one_shot_view_start_animation( + animation_manager->one_shot_view, XTREME_ASSETS()->A_Levelup_128x64); } static void animation_manager_switch_to_animation_view(AnimationManager* animation_manager) { diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index a80a958a3..4263dc0a4 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -40,7 +40,7 @@ void animation_handler_select_manifest() { furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { - FURI_LOG_I(TAG, "Custom Manifest selected"); + FURI_LOG_I(TAG, "Custom manifest selected"); } else { use_asset_pack = false; } @@ -48,14 +48,8 @@ void animation_handler_select_manifest() { } if(!use_asset_pack) { furi_string_set(anim_dir, BASE_ANIMATION_DIR); - if(xtreme_settings->nsfw_mode) { - furi_string_cat_str(anim_dir, "/nsfw"); - FURI_LOG_I(TAG, "NSFW Manifest selected"); - } else { - furi_string_cat_str(anim_dir, "/sfw"); - FURI_LOG_I(TAG, "SFW Manifest selected"); - } furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); + FURI_LOG_I(TAG, "Base manifest selected"); } strlcpy(ANIMATION_DIR, furi_string_get_cstr(anim_dir), sizeof(ANIMATION_DIR)); strlcpy( diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 7840cd00a..b5b73668b 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -17,6 +17,8 @@ #include "helpers/pin_lock.h" #include "helpers/slideshow_filename.h" +#include "../../settings/xtreme_settings/xtreme_assets.h" + static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); static void desktop_start_auto_lock_timer(Desktop*); @@ -305,6 +307,9 @@ static bool desktop_check_file_flag(const char* flag_path) { int32_t desktop_srv(void* p) { UNUSED(p); + // TODO: find a (working) way to run this at startup without hooking desktop + XTREME_ASSETS_LOAD(); + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { FURI_LOG_W("Desktop", "Desktop load skipped. Device is in special startup mode."); } else { diff --git a/applications/services/desktop/scenes/desktop_scene_fault.c b/applications/services/desktop/scenes/desktop_scene_fault.c index e4f5e788f..c2149253c 100644 --- a/applications/services/desktop/scenes/desktop_scene_fault.c +++ b/applications/services/desktop/scenes/desktop_scene_fault.c @@ -1,7 +1,7 @@ #include #include "../desktop_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define DesktopFaultEventExit 0x00FF00FF @@ -15,7 +15,7 @@ void desktop_scene_fault_on_enter(void* context) { Popup* popup = desktop->hw_mismatch_popup; popup_set_context(popup, desktop); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { popup_set_header( popup, "Slut passed out\n but is now back", diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index f0430de5d..450c5af23 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -40,7 +40,7 @@ static void render_callback(Canvas* canvas, void* _ctx) { const char* mood_str = NULL; const Icon* portrait = NULL; - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { if(stats->butthurt <= 4) { portrait = xtreme_assets->I_passport_happy_46x49; mood_str = "Status: Wet"; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index ecab8c333..6fd26138b 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -12,7 +12,7 @@ void power_settings_scene_power_off_on_enter(void* context) { DialogEx* dialog = app->dialog; dialog_ex_set_header(dialog, "Turn Off Device?", 64, 2, AlignCenter, AlignTop); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { dialog_ex_set_text( dialog, " I will be\nwaiting for\n you master", 78, 16, AlignLeft, AlignTop); } else { diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 3323cf1df..ba5e65ca0 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -3,15 +3,6 @@ #include #include -static void xtreme_settings_scene_start_base_graphics_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "NSFW" : "SFW"); - XTREME_SETTINGS()->nsfw_mode = value; - app->settings_changed = true; - app->assets_changed = true; -} - static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -172,11 +163,6 @@ void xtreme_settings_scene_start_on_enter(void* context) { storage_file_free(folder); furi_record_close(RECORD_STORAGE); - item = variable_item_list_add( - var_item_list, "Base Graphics", 2, xtreme_settings_scene_start_base_graphics_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->nsfw_mode); - variable_item_set_current_value_text(item, xtreme_settings->nsfw_mode ? "NSFW" : "SFW"); - item = variable_item_list_add( var_item_list, "Asset Pack", diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 0f6ab998d..13014b8d1 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -2,175 +2,50 @@ #include "assets_icons.h" #include +#define ICONS_FMT PACKS_DIR "/%s/Icons/%s" + XtremeAssets* xtreme_assets = NULL; -XtremeAssets* XTREME_ASSETS() { - if(xtreme_assets == NULL) { - XTREME_ASSETS_LOAD(); - } - return xtreme_assets; +void anim(const Icon** replace, const char* name, FuriString* path, File* file) { + do { + furi_string_printf(path, ICONS_FMT "/meta", XTREME_SETTINGS()->asset_pack, name); + if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) + break; + int32_t width, height, frame_rate, frame_count; + storage_file_read(file, &width, 4); + storage_file_read(file, &height, 4); + storage_file_read(file, &frame_rate, 4); + storage_file_read(file, &frame_count, 4); + storage_file_close(file); + + Icon* icon = malloc(sizeof(Icon)); + FURI_CONST_ASSIGN(icon->width, width); + FURI_CONST_ASSIGN(icon->height, height); + FURI_CONST_ASSIGN(icon->frame_rate, frame_rate); + FURI_CONST_ASSIGN(icon->frame_count, frame_count); + icon->frames = malloc(sizeof(const uint8_t*) * icon->frame_count); + const char* pack = XTREME_SETTINGS()->asset_pack; + + bool ok = true; + for(int i = 0; ok && i < icon->frame_count; ++i) { + ok = false; + furi_string_printf(path, ICONS_FMT "/frame_%02d.bm", pack, name, i); + if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) + break; + + uint64_t size = storage_file_size(file); + FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(size)); + if(storage_file_read(file, (void*)icon->frames[i], size) == size) ok = true; + storage_file_close(file); + } + if(!ok) break; + + *replace = icon; + } while(false); } -void XTREME_ASSETS_LOAD() { - if(xtreme_assets != NULL) return; - - xtreme_assets = malloc(sizeof(XtremeAssets)); - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - - if(xtreme_settings->nsfw_mode) { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; - xtreme_assets->I_passport_bad_46x49 = &I_flipper; - xtreme_assets->I_passport_DB = &I_passport_DB; - xtreme_assets->I_passport_happy_46x49 = &I_flipper; - xtreme_assets->I_passport_okay_46x49 = &I_flipper; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; - xtreme_assets->I_Error_62x31 = &I_Error_62x31; - } else { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = - &I_iButtonDolphinVerySuccess_108x52_sfw; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw; - xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw; - xtreme_assets->I_passport_DB = &I_passport_DB_sfw; - xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw; - xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw; - xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw; - } - - if(xtreme_settings->asset_pack[0] == '\0') return; - FileInfo info; - FuriString* path = furi_string_alloc(); - const char* pack = xtreme_settings->asset_pack; - furi_string_printf(path, PACKS_DIR "/%s", pack); - Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && - info.flags & FSF_DIRECTORY) { - File* file = storage_file_alloc(storage); - - swap_bmx_icon( - &xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_DolphinCommon_56x48, - pack, - "Dolphin/DolphinCommon_56x48.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinMafia_115x62, - pack, - "iButton/DolphinMafia_115x62.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_iButtonDolphinVerySuccess_108x52, - pack, - "iButton/iButtonDolphinVerySuccess_108x52.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinReadingSuccess_59x63, - pack, - "Infrared/DolphinReadingSuccess_59x63.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_NFC_dolphin_emulation_47x61, - pack, - "NFC/NFC_dolphin_emulation_47x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_passport_bad_46x49, - pack, - "Passport/passport_bad_46x49.bmx", - path, - file); - swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_passport_happy_46x49, - pack, - "Passport/passport_happy_46x49.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_passport_okay_46x49, - pack, - "Passport/passport_okay_46x49.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinReceive_97x61, - pack, - "RFID/RFIDDolphinReceive_97x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinSend_97x61, - pack, - "RFID/RFIDDolphinSend_97x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinSuccess_108x57, - pack, - "RFID/RFIDDolphinSuccess_108x57.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file); - - storage_file_free(file); - } - furi_record_close(RECORD_STORAGE); - furi_string_free(path); -} - -void swap_bmx_icon( - const Icon** replace, - const char* pack, - const char* name, - FuriString* path, - File* file) { - furi_string_printf(path, PACKS_DIR "/%s/Icons/%s", pack, name); +void icon(const Icon** replace, const char* name, FuriString* path, File* file) { + furi_string_printf(path, ICONS_FMT ".bmx", XTREME_SETTINGS()->asset_pack, name); if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t size = storage_file_size(file) - 8; int32_t width, height; @@ -190,3 +65,80 @@ void swap_bmx_icon( storage_file_close(file); } } + +void swap(XtremeAssets* x, FuriString* p, File* f) { + anim(&x->A_Levelup_128x64, "Animations/Levelup_128x64", p, f); + icon(&x->I_BLE_Pairing_128x64, "BLE/BLE_Pairing_128x64", p, f); + icon(&x->I_DolphinCommon_56x48, "Dolphin/DolphinCommon_56x48", p, f); + icon(&x->I_DolphinMafia_115x62, "iButton/DolphinMafia_115x62", p, f); + icon(&x->I_DolphinNice_96x59, "iButton/DolphinNice_96x59", p, f); + icon(&x->I_DolphinWait_61x59, "iButton/DolphinWait_61x59", p, f); + icon(&x->I_iButtonDolphinVerySuccess_108x52, "iButton/iButtonDolphinVerySuccess_108x52", p, f); + icon(&x->I_DolphinReadingSuccess_59x63, "Infrared/DolphinReadingSuccess_59x63", p, f); + icon(&x->I_NFC_dolphin_emulation_47x61, "NFC/NFC_dolphin_emulation_47x61", p, f); + icon(&x->I_passport_bad_46x49, "Passport/passport_bad_46x49", p, f); + icon(&x->I_passport_DB, "Passport/passport_DB", p, f); + icon(&x->I_passport_happy_46x49, "Passport/passport_happy_46x49", p, f); + icon(&x->I_passport_okay_46x49, "Passport/passport_okay_46x49", p, f); + icon(&x->I_RFIDDolphinReceive_97x61, "RFID/RFIDDolphinReceive_97x61", p, f); + icon(&x->I_RFIDDolphinSend_97x61, "RFID/RFIDDolphinSend_97x61", p, f); + icon(&x->I_RFIDDolphinSuccess_108x57, "RFID/RFIDDolphinSuccess_108x57", p, f); + icon(&x->I_Cry_dolph_55x52, "Settings/Cry_dolph_55x52", p, f); + icon(&x->I_Scanning_123x52, "SubGhz/Scanning_123x52", p, f); + icon(&x->I_Auth_62x31, "U2F/Auth_62x31", p, f); + icon(&x->I_Connect_me_62x31, "U2F/Connect_me_62x31", p, f); + icon(&x->I_Connected_62x31, "U2F/Connected_62x31", p, f); + icon(&x->I_Error_62x31, "U2F/Error_62x31", p, f); +} + +void XTREME_ASSETS_LOAD() { + if(xtreme_assets != NULL) return; + + xtreme_assets = malloc(sizeof(XtremeAssets)); + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + + xtreme_assets->A_Levelup_128x64 = &A_Levelup_128x64; + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; + xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; + xtreme_assets->I_passport_bad_46x49 = &I_passport_bad_46x49; + xtreme_assets->I_passport_DB = &I_passport_DB; + xtreme_assets->I_passport_happy_46x49 = &I_passport_happy_46x49; + xtreme_assets->I_passport_okay_46x49 = &I_passport_okay_46x49; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; + xtreme_assets->I_Error_62x31 = &I_Error_62x31; + + if(xtreme_settings->asset_pack[0] == '\0') return; + xtreme_assets->is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; + FileInfo info; + FuriString* path = furi_string_alloc(); + furi_string_printf(path, PACKS_DIR "/%s", xtreme_settings->asset_pack); + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && + info.flags & FSF_DIRECTORY) { + File* file = storage_file_alloc(storage); + swap(xtreme_assets, path, file); + storage_file_free(file); + } + furi_record_close(RECORD_STORAGE); + furi_string_free(path); +} + +XtremeAssets* XTREME_ASSETS() { + if(xtreme_assets == NULL) { + XTREME_ASSETS_LOAD(); + } + return xtreme_assets; +} diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index c49f5b590..038372a43 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -7,6 +7,8 @@ #define PACKS_DIR EXT_PATH("dolphin_custom") typedef struct { + bool is_nsfw; + const Icon* A_Levelup_128x64; const Icon* I_BLE_Pairing_128x64; const Icon* I_DolphinCommon_56x48; const Icon* I_DolphinMafia_115x62; @@ -30,15 +32,6 @@ typedef struct { const Icon* I_Error_62x31; } XtremeAssets; -XtremeAssets* XTREME_ASSETS(); - void XTREME_ASSETS_LOAD(); -void swap_bmx_icon( - const Icon** replace, - const char* base, - const char* name, - FuriString* path, - File* file); - -void free_bmx_icon(Icon* icon); +XtremeAssets* XTREME_ASSETS(); diff --git a/applications/settings/xtreme_settings/xtreme_settings.h b/applications/settings/xtreme_settings/xtreme_settings.h index ee0ec5583..13bb574ad 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.h +++ b/applications/settings/xtreme_settings/xtreme_settings.h @@ -18,7 +18,6 @@ typedef struct { int32_t cycle_anims; bool unlock_anims; - bool nsfw_mode; char asset_pack[MAX_PACK_NAME_LEN]; BatteryStyle battery_style; uint16_t anim_speed; diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_1/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_10/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_11/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_12/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_13/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_14/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_15/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_16/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_17/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_18/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_19/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_2/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_20/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_21/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_50.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_50.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_51.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_51.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_52.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_52.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_53.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_53.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_54.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_54.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_55.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_55.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_56.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_56.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_57.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_57.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_58.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_58.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_59.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_59.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_22/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_23/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_24/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_25/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_26/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_27/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_28/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_50.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_50.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_51.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_51.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_29/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_3/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_30/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_4/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_5/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_6/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_7/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_8/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_9/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt diff --git a/assets/dolphin/external/nsfw/manifest.txt b/assets/dolphin/custom/NSFW/Anims/manifest.txt similarity index 100% rename from assets/dolphin/external/nsfw/manifest.txt rename to assets/dolphin/custom/NSFW/Anims/manifest.txt diff --git a/assets/icons/Animations/Levelup1_128x64/frame_00.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_00.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_01.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_01.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_02.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_02.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_03.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_03.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_04.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_04.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_05.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_05.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_06.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_06.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_07.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_07.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_08.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_08.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_09.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_09.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_10.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_10.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_11.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_11.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_12.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_12.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_13.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_13.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_14.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_14.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_15.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_15.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_16.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_16.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_17.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_17.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_18.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_18.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_19.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_19.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_20.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_20.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_21.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_21.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_22.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_22.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_23.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_23.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_24.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_24.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_25.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_25.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_26.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_26.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_27.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_27.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_28.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_28.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_29.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_29.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_30.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_30.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_31.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_31.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_rate b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_rate rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate diff --git a/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png b/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png new file mode 100644 index 0000000000000000000000000000000000000000..f60598005d41ff05a9f763f42f1a6b7900150e33 GIT binary patch literal 2610 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 Ud8X1kK>z>%07*qoM6N<$g8GWm`2YX_ literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png b/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png new file mode 100644 index 0000000000000000000000000000000000000000..e80fea5bd7f694549da1b45e9f3db056342a95cc GIT binary patch literal 3376 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png new file mode 100644 index 0000000000000000000000000000000000000000..e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f GIT binary patch literal 4224 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Μe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png b/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png new file mode 100644 index 0000000000000000000000000000000000000000..98d7e15f9c105cc36c1f6d3473fd915a9687968b GIT binary patch literal 6373 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Μe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..2528ebc95d79335975511a384c70c010d476a8c0 GIT binary patch literal 4862 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..fef503263fd962534e7e5543954612bf6c1faefd GIT binary patch literal 4882 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png new file mode 100644 index 0000000000000000000000000000000000000000..78c4e93f8a8c4e94eb0513c7b59f6dde68748205 GIT binary patch literal 4466 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 Ef`SCamH+?% literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png b/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png new file mode 100644 index 0000000000000000000000000000000000000000..d1164c59483a2242769327b5b2c9ac01acb83186 GIT binary patch literal 3798 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 M07*qoM6N<$g8AuxBLDyZ literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png b/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png new file mode 100644 index 0000000000000000000000000000000000000000..a48c5330e85c2135866fe99bcb6f3534d06c6d53 GIT binary patch literal 4092 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png b/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png new file mode 100644 index 0000000000000000000000000000000000000000..bc1010ca9da500055615c00359ea7536df4caa01 GIT binary patch literal 1874 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 Bfj=Do809goM&!5F$zl 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 Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png b/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png new file mode 100644 index 0000000000000000000000000000000000000000..4beec55efbc4a6907e37e5d7422b9f7c33d3dbf1 GIT binary patch literal 5122 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png new file mode 100644 index 0000000000000000000000000000000000000000..90b589ff8e9f18b28d7fd75ca525a1db24381b06 GIT binary patch literal 4719 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png b/assets/dolphin/external/L1_Boxing_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png b/assets/dolphin/external/L1_Boxing_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png b/assets/dolphin/external/L1_Boxing_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png b/assets/dolphin/external/L1_Boxing_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png b/assets/dolphin/external/L1_Boxing_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png b/assets/dolphin/external/L1_Boxing_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png b/assets/dolphin/external/L1_Boxing_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt b/assets/dolphin/external/L1_Boxing_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt rename to assets/dolphin/external/L1_Boxing_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png b/assets/dolphin/external/L1_Cry_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png rename to assets/dolphin/external/L1_Cry_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png b/assets/dolphin/external/L1_Cry_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png rename to assets/dolphin/external/L1_Cry_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png b/assets/dolphin/external/L1_Cry_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png rename to assets/dolphin/external/L1_Cry_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png b/assets/dolphin/external/L1_Cry_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png rename to assets/dolphin/external/L1_Cry_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png b/assets/dolphin/external/L1_Cry_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png rename to assets/dolphin/external/L1_Cry_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png b/assets/dolphin/external/L1_Cry_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png rename to assets/dolphin/external/L1_Cry_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png b/assets/dolphin/external/L1_Cry_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png rename to assets/dolphin/external/L1_Cry_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png b/assets/dolphin/external/L1_Cry_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png rename to assets/dolphin/external/L1_Cry_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt b/assets/dolphin/external/L1_Cry_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt rename to assets/dolphin/external/L1_Cry_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt b/assets/dolphin/external/L1_Furippa1_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt rename to assets/dolphin/external/L1_Furippa1_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt b/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt rename to assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png b/assets/dolphin/external/L1_Laptop_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png b/assets/dolphin/external/L1_Laptop_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png b/assets/dolphin/external/L1_Laptop_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png b/assets/dolphin/external/L1_Laptop_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png b/assets/dolphin/external/L1_Laptop_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png b/assets/dolphin/external/L1_Laptop_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png b/assets/dolphin/external/L1_Laptop_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png b/assets/dolphin/external/L1_Laptop_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt b/assets/dolphin/external/L1_Laptop_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt rename to assets/dolphin/external/L1_Laptop_128x51/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt b/assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt rename to assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt b/assets/dolphin/external/L1_Mad_fist_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt rename to assets/dolphin/external/L1_Mad_fist_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png b/assets/dolphin/external/L1_Mods_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png rename to assets/dolphin/external/L1_Mods_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png b/assets/dolphin/external/L1_Mods_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png rename to assets/dolphin/external/L1_Mods_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png b/assets/dolphin/external/L1_Mods_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png rename to assets/dolphin/external/L1_Mods_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png b/assets/dolphin/external/L1_Mods_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png rename to assets/dolphin/external/L1_Mods_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png b/assets/dolphin/external/L1_Mods_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png rename to assets/dolphin/external/L1_Mods_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png b/assets/dolphin/external/L1_Mods_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png rename to assets/dolphin/external/L1_Mods_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png b/assets/dolphin/external/L1_Mods_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png rename to assets/dolphin/external/L1_Mods_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png b/assets/dolphin/external/L1_Mods_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png rename to assets/dolphin/external/L1_Mods_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png b/assets/dolphin/external/L1_Mods_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png rename to assets/dolphin/external/L1_Mods_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png b/assets/dolphin/external/L1_Mods_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png rename to assets/dolphin/external/L1_Mods_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png b/assets/dolphin/external/L1_Mods_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png rename to assets/dolphin/external/L1_Mods_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png b/assets/dolphin/external/L1_Mods_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png rename to assets/dolphin/external/L1_Mods_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png b/assets/dolphin/external/L1_Mods_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png rename to assets/dolphin/external/L1_Mods_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png b/assets/dolphin/external/L1_Mods_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png rename to assets/dolphin/external/L1_Mods_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png b/assets/dolphin/external/L1_Mods_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png rename to assets/dolphin/external/L1_Mods_128x64/frame_21.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png b/assets/dolphin/external/L1_Mods_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png rename to assets/dolphin/external/L1_Mods_128x64/frame_22.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png b/assets/dolphin/external/L1_Mods_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png rename to assets/dolphin/external/L1_Mods_128x64/frame_23.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png b/assets/dolphin/external/L1_Mods_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png rename to assets/dolphin/external/L1_Mods_128x64/frame_24.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png b/assets/dolphin/external/L1_Mods_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png rename to assets/dolphin/external/L1_Mods_128x64/frame_25.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png b/assets/dolphin/external/L1_Mods_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png rename to assets/dolphin/external/L1_Mods_128x64/frame_26.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png b/assets/dolphin/external/L1_Mods_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png rename to assets/dolphin/external/L1_Mods_128x64/frame_27.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png b/assets/dolphin/external/L1_Mods_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png rename to assets/dolphin/external/L1_Mods_128x64/frame_28.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png b/assets/dolphin/external/L1_Mods_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png rename to assets/dolphin/external/L1_Mods_128x64/frame_29.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png b/assets/dolphin/external/L1_Mods_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png rename to assets/dolphin/external/L1_Mods_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png b/assets/dolphin/external/L1_Mods_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png rename to assets/dolphin/external/L1_Mods_128x64/frame_30.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png b/assets/dolphin/external/L1_Mods_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png rename to assets/dolphin/external/L1_Mods_128x64/frame_31.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png b/assets/dolphin/external/L1_Mods_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png rename to assets/dolphin/external/L1_Mods_128x64/frame_32.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png b/assets/dolphin/external/L1_Mods_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png rename to assets/dolphin/external/L1_Mods_128x64/frame_33.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png b/assets/dolphin/external/L1_Mods_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png rename to assets/dolphin/external/L1_Mods_128x64/frame_34.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png b/assets/dolphin/external/L1_Mods_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png rename to assets/dolphin/external/L1_Mods_128x64/frame_35.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png b/assets/dolphin/external/L1_Mods_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png rename to assets/dolphin/external/L1_Mods_128x64/frame_36.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png b/assets/dolphin/external/L1_Mods_128x64/frame_37.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png rename to assets/dolphin/external/L1_Mods_128x64/frame_37.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png b/assets/dolphin/external/L1_Mods_128x64/frame_38.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png rename to assets/dolphin/external/L1_Mods_128x64/frame_38.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png b/assets/dolphin/external/L1_Mods_128x64/frame_39.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png rename to assets/dolphin/external/L1_Mods_128x64/frame_39.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png b/assets/dolphin/external/L1_Mods_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png rename to assets/dolphin/external/L1_Mods_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png b/assets/dolphin/external/L1_Mods_128x64/frame_40.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png rename to assets/dolphin/external/L1_Mods_128x64/frame_40.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png b/assets/dolphin/external/L1_Mods_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png rename to assets/dolphin/external/L1_Mods_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png b/assets/dolphin/external/L1_Mods_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png rename to assets/dolphin/external/L1_Mods_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png b/assets/dolphin/external/L1_Mods_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png rename to assets/dolphin/external/L1_Mods_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png b/assets/dolphin/external/L1_Mods_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png rename to assets/dolphin/external/L1_Mods_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png b/assets/dolphin/external/L1_Mods_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png rename to assets/dolphin/external/L1_Mods_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt b/assets/dolphin/external/L1_Mods_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt rename to assets/dolphin/external/L1_Mods_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png b/assets/dolphin/external/L1_Painting_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png rename to assets/dolphin/external/L1_Painting_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png b/assets/dolphin/external/L1_Painting_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png rename to assets/dolphin/external/L1_Painting_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png b/assets/dolphin/external/L1_Painting_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png rename to assets/dolphin/external/L1_Painting_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png b/assets/dolphin/external/L1_Painting_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png rename to assets/dolphin/external/L1_Painting_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png b/assets/dolphin/external/L1_Painting_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png rename to assets/dolphin/external/L1_Painting_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png b/assets/dolphin/external/L1_Painting_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png rename to assets/dolphin/external/L1_Painting_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png b/assets/dolphin/external/L1_Painting_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png rename to assets/dolphin/external/L1_Painting_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png b/assets/dolphin/external/L1_Painting_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png rename to assets/dolphin/external/L1_Painting_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png b/assets/dolphin/external/L1_Painting_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png rename to assets/dolphin/external/L1_Painting_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png b/assets/dolphin/external/L1_Painting_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png rename to assets/dolphin/external/L1_Painting_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png b/assets/dolphin/external/L1_Painting_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png rename to assets/dolphin/external/L1_Painting_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png b/assets/dolphin/external/L1_Painting_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png rename to assets/dolphin/external/L1_Painting_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt b/assets/dolphin/external/L1_Painting_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt rename to assets/dolphin/external/L1_Painting_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png b/assets/dolphin/external/L1_Read_books_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png b/assets/dolphin/external/L1_Read_books_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png b/assets/dolphin/external/L1_Read_books_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png b/assets/dolphin/external/L1_Read_books_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png b/assets/dolphin/external/L1_Read_books_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png b/assets/dolphin/external/L1_Read_books_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png b/assets/dolphin/external/L1_Read_books_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png b/assets/dolphin/external/L1_Read_books_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png b/assets/dolphin/external/L1_Read_books_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt b/assets/dolphin/external/L1_Read_books_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt rename to assets/dolphin/external/L1_Read_books_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png b/assets/dolphin/external/L1_Recording_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png rename to assets/dolphin/external/L1_Recording_128x51/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png b/assets/dolphin/external/L1_Recording_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png rename to assets/dolphin/external/L1_Recording_128x51/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png b/assets/dolphin/external/L1_Recording_128x51/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png rename to assets/dolphin/external/L1_Recording_128x51/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png b/assets/dolphin/external/L1_Recording_128x51/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png rename to assets/dolphin/external/L1_Recording_128x51/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png b/assets/dolphin/external/L1_Recording_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png rename to assets/dolphin/external/L1_Recording_128x51/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png b/assets/dolphin/external/L1_Recording_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png rename to assets/dolphin/external/L1_Recording_128x51/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png b/assets/dolphin/external/L1_Recording_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png rename to assets/dolphin/external/L1_Recording_128x51/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png b/assets/dolphin/external/L1_Recording_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png rename to assets/dolphin/external/L1_Recording_128x51/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png b/assets/dolphin/external/L1_Recording_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png rename to assets/dolphin/external/L1_Recording_128x51/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png b/assets/dolphin/external/L1_Recording_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png rename to assets/dolphin/external/L1_Recording_128x51/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png b/assets/dolphin/external/L1_Recording_128x51/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png rename to assets/dolphin/external/L1_Recording_128x51/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png b/assets/dolphin/external/L1_Recording_128x51/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png rename to assets/dolphin/external/L1_Recording_128x51/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt b/assets/dolphin/external/L1_Recording_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt rename to assets/dolphin/external/L1_Recording_128x51/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png b/assets/dolphin/external/L1_Sleep_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png b/assets/dolphin/external/L1_Sleep_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png b/assets/dolphin/external/L1_Sleep_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png b/assets/dolphin/external/L1_Sleep_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt b/assets/dolphin/external/L1_Sleep_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt rename to assets/dolphin/external/L1_Sleep_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt b/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt rename to assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png b/assets/dolphin/external/L1_Waves_128x50/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png rename to assets/dolphin/external/L1_Waves_128x50/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png b/assets/dolphin/external/L1_Waves_128x50/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png rename to assets/dolphin/external/L1_Waves_128x50/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png b/assets/dolphin/external/L1_Waves_128x50/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png rename to assets/dolphin/external/L1_Waves_128x50/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png b/assets/dolphin/external/L1_Waves_128x50/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png rename to assets/dolphin/external/L1_Waves_128x50/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt b/assets/dolphin/external/L1_Waves_128x50/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt rename to assets/dolphin/external/L1_Waves_128x50/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt b/assets/dolphin/external/L2_Furippa2_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt rename to assets/dolphin/external/L2_Furippa2_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt b/assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt rename to assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png b/assets/dolphin/external/L2_Soldering_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png b/assets/dolphin/external/L2_Soldering_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png b/assets/dolphin/external/L2_Soldering_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png b/assets/dolphin/external/L2_Soldering_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png b/assets/dolphin/external/L2_Soldering_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png b/assets/dolphin/external/L2_Soldering_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png b/assets/dolphin/external/L2_Soldering_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png b/assets/dolphin/external/L2_Soldering_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png b/assets/dolphin/external/L2_Soldering_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png b/assets/dolphin/external/L2_Soldering_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png b/assets/dolphin/external/L2_Soldering_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt b/assets/dolphin/external/L2_Soldering_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt rename to assets/dolphin/external/L2_Soldering_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt b/assets/dolphin/external/L2_Wake_up_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt rename to assets/dolphin/external/L2_Wake_up_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt b/assets/dolphin/external/L3_Furippa3_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt rename to assets/dolphin/external/L3_Furippa3_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt b/assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt rename to assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt b/assets/dolphin/external/L3_Lab_research_128x54/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt rename to assets/dolphin/external/L3_Lab_research_128x54/meta.txt diff --git a/assets/dolphin/external/sfw/manifest.txt b/assets/dolphin/external/manifest.txt similarity index 100% rename from assets/dolphin/external/sfw/manifest.txt rename to assets/dolphin/external/manifest.txt diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png deleted file mode 100644 index bf97f8d6ea7ee9d0df8714b06ceb37d5231d9444..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1326 zcmaJ>eQ4Zd7|&g6?MkbT;lNE?$Og8M!GJD&u*4t@s!)f0n@AX1^NtUGE zwXE(eMNn}?w!u(_-xWbhL2;;HYyB7sbt+TrT-gM5)wQVqtVr4Fd%a%WA7vrQ`@CSAqj0<8t~M5`;%`?uBAdLU1l?)I_;8M>Nd*q&jT zc+nzItv)YIAhM+>vUWVaX4rIBbA_@-=YdVL6hmjT#n4u?T`vLur?IZKo9wqCoq(>@ z=V+)T>Fs4OO5e>L6%`R1=^@8JbpgGyrS)Z@>BZzQfxb|>p-VvkRRDlWY5|rdIVfl( zP}4lA1{nhYvZ6Hdfl!OWwKOwjY|L~$U}~`J z+d#fov5hyyO05nAp5-JhtGC<;9U05B+>B-898WQLeWzjSR?clG)~FP+3?1W1sPA&D zEWVOt$ykQ~;2RrxPK2b2I-L|$9Z9+xBt?M-X+h8gRz(4n@cIqOEA=TlK|$DME)v7S zbUFuXLm!ooP;(Z=wZTA}wb6nmnkNSh z&fzcZ4QS}9I`$?Nv@S0%zcus9EQa~0nB1JqpPxVZ%WYG5?eNcwM-1)m-TP;{=EwHW zU*o8MK6xZ_yPS9@x%0&Cx;@Xoeqr{kur%s_Z20uc2Oi#d_3Bpt^b>x}MeXBcF|a?fYY3 zU*Ux}E@Q7IW`dK~Dvw=1z3Fg!bc-_Hf7Um5{tMXm@6vSS7`7+(_V4dD%<|2PKXh(9 t_vj_AuwmrN!R4ChpjYcZd2uuCtHQXETaTW4F5FZ6gT<6~d90;t=s&rU%P0T< diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png deleted file mode 100644 index 39c910d3a41314c985e4ce79337cbbf30d1ae79e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1597 zcmaJ>eM}Q)7{5}5%D^dpRi@)O7`W*5?s^B;R;rYKC`z%G8Y+Q}>)l)EN_*Y)Sc`6o z=qAjeQ#WD)}yh;LPt4dw`DnG9mu!RfIf~Wx+2mqNwqk+0$*bp^hBX$i?jvW&i zI-(-`jo2tDx7~r7MG2s+hQfK0BvDqc0RpG>&|Z$8hiXYJMNsf&af&r)X#=H4$1V&y zlmxHAX|avDgF7QuE6X7RK|~@EO@!8nQVl`r^?HKT5?U<|HSlmlQ0Ag|Fq{>)U;$xX z@`Yrd7(`SmTZhvAp7#_;NdvV zgY$V5g=+!BA+9YtBqdTWuzYR#wf^hH)} zk%^3Bi-TdA3-TcD+XsD*VNK7)8q5;lWKnX7VqLru9JQh>hHJ$Ts--m~I;Y$h6vRmQ z)mV*2L$(0P_ZARrk{Ccol5Frjg8)b8=hIpi;2zlNIO7qZBzSZ<%g~&cVR(k}zyL<^ z!c)l`?NfLHg9ydB7)T0u^LR2J2Y}JzJj+nHhbFbSmt$F)CMg}muowX;60yE{s*mDF zusLJmlfi(SC!+;|aGoSMIBLq(1&1!tQR1?~b$EDq|K+_`5G27^Y%#f_f823bZBb^b zgnx(k{&b_X%C@ij%eKlR1xz4AxW=oeHrzdS@wL@=Po(b|Y|8y{=6&U!_b23JK6qSl z=Ul(3U+Z>w81&36ROoa>Vw};AiR+k0+1z%t8NFY0jwL6AiTw!i~cvHBP6VAaH(t%~zvICA{Agl`I%Vxu z68zSrmC0!dz3ofM=AK;@KX*2Lw5i2p+ufM1m_OK}@82}P>xjFuZtgeW@!@ny9%#*) zwmG$JIs3-vs-E7n-E*90d}+P&5)voBGI-Sa>GrGlauUsV&W&$Ps>}&tmW&aP|S$*a?s)anPWU)!;^ zL#VILC2LNl&CYCZzS55@y|-=+x*uu2(bC40EMboCNF}Cqe)a1irm04ewgSz*9By^% z53in~QmPX@4W{I>O!-`%ZUS=a#>U^Lq$RausA>A~+>cfMi{lcGP4_BNmUXd;&QI)Z z==qd;({~>{d1mb{bt%%QGTu(SaN34cx>QKxEJgeM{Y=w%WCnXCBX!LKlhR`U;qFB diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png deleted file mode 100644 index 4975adf8627b62a22d002bfc1d7be423dcc5e83b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1754 zcmaJ?c~BE)6kiEqM1gwIQV`cr5o$NPn}ZyZawR~JAjTlrk&(@2V^T;qCJO`*jYwN? zzJ0&UL^uF2iuI~ADov3n#z_MkbLz0I1`~}2fba;b3Bxl93RDvt^hOD5;Pe?5sMknX zseA>ZFvSo$eSE%|Se>7w!t*n6k%krVDHv`Q(*_I#g@IN>meC@%N>~$i#kA}^hFRc* z3Y95gO_EAeB!e-enE(ZB6v7b%0R;fhfaICZgT zhh~jdtdz!1xubUymX4xKVi?ZN&1L6u*`#>`jEY1e7~#Mi4n%7}mOLYcSs|k($YnuF zSa7r6MCnN*=(LEb$!to(qRCE1VK6BaZwwnPQ@NtE23s)`jIt5fU~q;up>3g*#D6s2 zscli^nFv@(SjcQMPM1U*c(#EsQgW$;MSo#y^ct}c zBh(m%sUe(?q7aAR;Si?Qav`-4!7-r*!?^;X%g(#-B9TB8%jHA~I5HuM%3?&aXsJvV z#^sBmWt`|J#D$d`Efi+N372m@?RyF<`d_R##!O%oX;zVBma7qxbtFYvbfgL7aM=hL zlBzdq$XrXPvqqC4O9`_+htR~DNdq{MWU>Ao1Tbz`7?&d;AT{0T5Kpb4C5>7L33yyg z%j4lZR80pkiPyZ9yvaVLPv{^_E-qpOCA54T34;iNCxUPR4~5iRgac_Yfq=_JP$7>e zV8Jv+*x5I4^^xm{ZceB8W-#c@o6#bS^gNmA!MWz%;YAOfM{=S{Mz0Qs<3MNiJpgd) zl1rmh)~^1wcgr6m%w~t9I_nxTE`~My=&O-8 z^gpbe?g8|F%k{EVr;;O0dg4dtG7da+A?1Toh{ro$cLX)S$u4NTs#)APzctwFBm`J-;zH4 zG~j+QWNYc%rB@myO~`n2pzYl30R}T3&QPu?0OthkE<)vN@N<2Uvm*Pq+pGAEmix8S=nd9m#njz_o z)mNE70C33D#l~~-VT%Zg)r&~Gj>xJv@@N1ER9KB9wTj_D4U?=h$b~)C7lfdWmJ8$j z!;mmz2$P~)kZEFKGQ(r3%vF?(7Aod~fmVVypl3J|wCYn0X2L2LzP3y7vi%qqg0EG$ zRdV4Vsrax+FoZQRATB~73PBJMmx&mf#57_pNzDc^1Vdqzzi|k~2~13&GH~b;@(xY3 zmWWoWhurZyxiE#}j06l@EEbVPEMiT`Fe;PDU<89P4B|B)bB2Kugd(LhNr`dku$_0|Wini*7Go+LQ%O-&6(UmwD^;ohvA--> zg$2(;hOwarGe;UIX4tom_dSGFz86b`m>7~{O|dMSI@}16DJ;jDQ&=O2iA4zL8>chS zti|kSuhC%0O2(v1XK1yF)q}5-Oz7T0fD{J=h%uajG<>H+5)I8uv_=Zy5;3WjNGJ)a z;R6`N({Ckjuuu6DJ_zG57Xm>Utv`hXKnx?1K@=`QA&nTpAT5dGVljeBB@$c+^Aurw z-@MgF!$*8`+Qm16!EfG-7GvP&$;1!Nn#4Z)*f~WmidFIJz`#IRsv7#R+qh3KqUKV$YLpsF+-C>Ad1 zW5>=dVf}1sI?^UO=z_-BRDvKiqR!gx~J?s>wfxY|O9R z=3<%TTyv!--`al0bHepClGHZaLthvA+^UGKm8UDa^XhJ&LO!34xwW?BwHEl^*cY-~ z<8`=PGTBGH@UQB_-Fu367GaZe+zy?sU%%opS!1ew<>HilXz7!K%hYRTwIl^?#-}*f z1@ul;6k?`QBD!{0eq6!w)V()%oo;Mv&+BX<;EJruyw*DRnNDc~;QgB4(jyyYOl#V4 zKjtX6d&H=T8#b$@NiiP<{E*-MHL}KC;E)jSZWDr^Y&Z%6BZIC-Z|b#-?HQLFTsSJS z@$Q#_w%gOBZtV~6c9!ORQ`9$FQ0rII=XiRz?nsY&dH2H2<^3{|#OCdI(@=P&b)sPW zyMOp7w6(KXWo40nJ zY|eY}PX@?#b!4yazvau+ju3P#d)l$6(`iE4%ZY-9f5|pwy$K*30`J*m9hcRstx#3CvLZO?i~j&Y CQO3gn diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png deleted file mode 100644 index e6c88df92b73b89f1ad31810be1a653c3fea5d6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1686 zcmaJ=c~BE)6we_o2w0(7EJ|I2w$PgFkwB7#KnMwk5ag0$7>>iMhk9}@h1G@VD8w4dC-Ij zQz+w2lFCpgLXotYg2V!pPa+5ciKPNcOJEwIjvyC97=ocN3SKcE72}u?N2Soz!vg`$ zS{ z2o0YMLQy_OiAX-7(Fyq)2|^MQEkO#!5~rUR;iXcsG)gGM#F$EgqN+%#N}*J#f`vg+ zg$h$dAWm$o(ZUc$l5)n@gVBBu}%3uuP66woFJ6ABP0AYE_N z(pF2L=2MhN$|uDll&=vY7+*(-#X=#1N<<gi@&~&dP#;2;lXt&#&t~K0bu{c{|l@Y17 z>-RHn7{7%);{LVo+jqBhkqhwFC4EtW3*T<$zCId!FiEzI1fFWbckNm0!wC`F3?{`-k_IuBzxtei%cnRJ}L)M0R{}2G+6F zZG*9Y7OSOh-!=B>@A8IMxp8A|xO=x=!2Y(+gJ-d;8f8QB1>^h2=QL`k^?dIQyM-j| zc4sTxD_T;{yi;1W<)2XO1@7zLRZQU{WAjPZ3s_`D;x8k!Rx(r zKja`3H`3xc5OAPI-m|UaXIjwS9Q;g z|NJ!wnLSd#RzRm}F3x!OXko;eggHLEjEeb+)zxy`<>_YMQx@?^U1w9t;}c1K@m{7o zOMYbiaeBb(0uyc-z1&v+X;R|B+QqeBmLg>`K?T>>PuurH!->W=`<=?xPu9n?my3`3 z&mOS$&p(%)uDO_#>eK7qD+r0I9N;9mtXS)}Fnc&`tj4{o`pL{(#jZ!9gTHL`??_vJ zfKzP`85l_E${B5GU~kg*br<+>+p_$N<2uSbpn2@F!~NZyb)lDw!Z{wSWiB%sx(wTa PLRqX>Rf6(}B5%!qVpw+j diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png deleted file mode 100644 index e7bae4d6c8e7b60de8cb5d4751ae8d9259a39338..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1672 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 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png deleted file mode 100644 index 32e864e9827420142930083d480d331024d033f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1540 zcmaJ=eM}Q)7(ZwQL|`a!rcUQsm~or;U3zV$($W^GvxQU^s++UdySJr6@4EJ&1vM*% zeI;gS%*lpcNfsA0gc z;4`?ar4Pfwol#va%OL|nM59qW%HUGiPf&Wjo*-$0rZK32MH)nzi(z6UKk2~=B78Uy zk^_>6COmRpsZKVkA=u$8f+2_Fkz+CPuvV~YM2rg&6iyPsU?R04?TG9G|HF8qcBHZ) z1PB+1NOfTzw#1j8G=}ASdZ2_NL}Mrq2Vh^gTC2p@1%W8rtwuGx!G(Ze&~d=aahw<9 z3n>btfriI8uaCjJI+Evf0>?9~F6rk<_9djw!f16imbU3A%4X5qN~|`UmMPSi*ys{7 znY6Ww5t$Qtkc=IGvCV7=tEKhkr`j4UVZg~!xKfg8lSHVfmSibXErn2;!AW##RX`M^ zXynC2kA_pWf^eW72&G{uhz=Fm5O@Lrj?rovngy5_G9A-+1tgoGE;=1Mw!5dLIL>q&-FN=xw&*uC4Znihe$~NUAEd-L-0kus zvktd4b)`E?wVS_whgh+2EWSyhisJ_sDkr2?m?x%AIPEvRi)_AFgWT)A-;$?9Zk`oV zLVYi%ps58@;_dEc&+@jWF$=~m*faKDX(zHZXH?WP`ciFt`c{|Fvuq@?>tuWXt{Vb! z&3Wz*d$02K6q9mm8tck5QLDT1*md>GvZ{VYZ1vUYuDk-xr(((|(;@q++&ZNF1LPs~qy>5Nmh?FjI;C+jq@u}~$RyFRM)b3xp;ltj4 zUf7$r<(&U% zE4lk;thumebThty%=r6+vRkAVy);)DQv5hN>$`!&x?{7-dKcfxHhtdGbotBIkc-zp zt~M^wT)%m*{C>Ksr98cC%PMNJYx@)?@A-Qd8ZCi=E6$PI?~k0V=*Uv$A3kUNA@v~h zY7sUVMW+zEytcAL|>t;DnRKT5(UD*ylh diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png deleted file mode 100644 index c692f48952c106373b56ecafd4bce7ba1295f5da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1557 zcmaJ>d2ka|7=JXS#89*jgNP!m+d9ax$sTDpS(;usXn;T{(Nd zzTbE48t1HOBQ$Sl001NG#g-DZE=Ny+T7}*n>U9pZj8Uv*%3Rr}MENiTgNH=xd5nEiLNrG%rTP_l7>HN@+n_tNGvR(k32{1IG#b@L8Lb?y)KNyGQAg4`nkJA25vh?BK1N89%%lYij0j;d zq=>SFB`oqDxmq#di0nWV!H~o8)UXs8%oWO-F2;v+l$O*5gNe}kwIfOi{GY~iwIi;Y z5Y&~xh+G{OP)U55Nia&^iyb8t5gTrHSVVQ<7g}VYItV4jZZYBLi`FZ8IRg(pJkNUw zA)BHI8tMgt_xKpXV;}|I;N=B|H6-mk4_`zY%uJ4vW@)Q|qO4}4wa8+%<}lgDA}d`~ zNG7p%DWdR_0F%B&&{G~1zyRL+ zO!5Z$6rG?TLP;(hNkLz>K;{q->WzfJ>M6p*kTl`rS(af)%AnV?xDHX&CHm%>K1v>; z=1hoB2Lo-Mjuw>AJcZHVd_T8g6B@cS=S-Ist?_t#_wU>O03c^_Er#$s`}2At6RG*kH%iwIIOcLFlAe36?42` zd`H8NV|%w=Ik>{cJos)yU)9OG6WtnTd|p~cdKc)+S^(;2&%4_4jr#TWo~>!v`$I_3 zvoG$iyP*Gc$nt#$mMrTc?^4j2(rjCMyj&wy2`SEwopXM!ZT;kwq%d6%v)^Xy1k%`}eLa8Mr}TBFU}|SM{nDF;MJcVm>CKkZ6<_sg3WvFCvsz!Sm}nk; z;m#rV%4Nqss3XP{gY-=s0@>?ZeiF@xx#8B{Yx# diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png deleted file mode 100644 index fb1c8bb9042d28b4e0ace883ea20e23cccf7ea52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1551 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 aR#*bwkfy%NiRRUj|9VblzU`o;xcWa;e@A@) diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate b/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate deleted file mode 100644 index 0cfbf0888..000000000 --- a/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png b/assets/icons/Animations/Levelup_128x64/frame_00.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png rename to assets/icons/Animations/Levelup_128x64/frame_00.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png b/assets/icons/Animations/Levelup_128x64/frame_01.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png rename to assets/icons/Animations/Levelup_128x64/frame_01.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png b/assets/icons/Animations/Levelup_128x64/frame_02.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png rename to assets/icons/Animations/Levelup_128x64/frame_02.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png b/assets/icons/Animations/Levelup_128x64/frame_03.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png rename to assets/icons/Animations/Levelup_128x64/frame_03.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png b/assets/icons/Animations/Levelup_128x64/frame_04.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png rename to assets/icons/Animations/Levelup_128x64/frame_04.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png b/assets/icons/Animations/Levelup_128x64/frame_05.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png rename to assets/icons/Animations/Levelup_128x64/frame_05.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png b/assets/icons/Animations/Levelup_128x64/frame_06.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png rename to assets/icons/Animations/Levelup_128x64/frame_06.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png b/assets/icons/Animations/Levelup_128x64/frame_07.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png rename to assets/icons/Animations/Levelup_128x64/frame_07.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png b/assets/icons/Animations/Levelup_128x64/frame_08.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png rename to assets/icons/Animations/Levelup_128x64/frame_08.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png b/assets/icons/Animations/Levelup_128x64/frame_09.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png rename to assets/icons/Animations/Levelup_128x64/frame_09.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png b/assets/icons/Animations/Levelup_128x64/frame_10.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png rename to assets/icons/Animations/Levelup_128x64/frame_10.png diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_rate b/assets/icons/Animations/Levelup_128x64/frame_rate similarity index 100% rename from assets/icons/Animations/Levelup1_128x64_sfw/frame_rate rename to assets/icons/Animations/Levelup_128x64/frame_rate diff --git a/assets/icons/BLE/BLE_Pairing_128x64.png b/assets/icons/BLE/BLE_Pairing_128x64.png index f60598005d41ff05a9f763f42f1a6b7900150e33..34068c300386e0c88e45b40a8995b0a4360ed79f 100644 GIT binary patch literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG delta 2594 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 Ef}^?D!2kdN diff --git a/assets/icons/BLE/BLE_Pairing_128x64_sfw.png b/assets/icons/BLE/BLE_Pairing_128x64_sfw.png deleted file mode 100644 index 34068c300386e0c88e45b40a8995b0a4360ed79f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG diff --git a/assets/icons/Dolphin/DolphinCommon_56x48.png b/assets/icons/Dolphin/DolphinCommon_56x48.png index e80fea5bd7f694549da1b45e9f3db056342a95cc..089aaed83507431993a76ca25d32fdd9664c1c84 100644 GIT binary patch literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj delta 3373 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 D!U>8P diff --git a/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png b/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63.png index 93a7ad79cd95cdfe7789366edd8fadb3355e467c..46f559f65f11194c94c15bb7f195a1d72ef2a295 100644 GIT binary patch literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX literal 5390 zcmV+p74hncP)pZz)3_wRCwC0+IQ4c)shA9vCR>4R?Gn~D zCQv~XL=-_#1QS7wh=_`cPr)28=bUrSX^af>>$~N8{lN9sdgJ+{>ArnJ)vjH;>hwqL zKmXRzqD71Un$wu$%#AkMC~yAdwszQb(@mQ+Y0|7&v)b{;AAjuMGz*|lre|DJ8M zZrwWHW*lFP{r21Mv(G+bM~4m_jyvwSV~#oIueR;py?dKBZRVeU{uf_-@%iVUukE|< zzW?}}#&XLo$B5f*yX~KqaqF$O-hco7bImo^LJKXl@WKmwU1NL>&pmg?9e33J_i4m*rx0|yT5+qdtA8*X^WA%_ebHf-som-g}Px8I)Q z&J!n2w9mQcp4+iw$6tQ=1(E(=7XS9!Z}8h-gAIE0=yAjmM~oUZ>XcJXS#rrG7hinw zC5y4d5=+2!wbfR;aYppe5!i2l;zMJXmth3Jk`|tnGJMX;o(n}4N3(*1#Ea0oh9(xRn z1kjv+$6`j@dFP#n9(t%#yL9Q&r%xZjoo~MR-gx7U*I$1Gr?^wCFm z>(&jr+itt9SUHq?wsnfNhz_07TyOADKx$0nz04(Zd+f2tF1zf~vSrIvR#^oFd0&Pp z=7M(U(4oD0^&$%d^84?Lj86SWAG0)due|;23bi`e8#TBIvXc0q>O3;TM zdT8Bs*JYF3XD$)I+RZoL{P4pMXY^&4UFOve9){3gef1Rv16V58_${67g)UD#@dO8` zv?rkY{PWLGKmGI|4I#Av?Ou816_b-s!nEqDtI}7mUwrY!mtTIl;DQUPVPHez_uqg2 z;fEi_1SeVRAq|108ipJHTMS>Vu)+#kZ@u*=pL`-RwxT)r-FF{PAc`t%MN!A`(6_6v zzIu&AlpId%3s(%_Ss;r=;nY)41qq1(B-cR&_;_z7r01S{ZpCx^VqhMQN~u<@T3O2t zhdue^lbPmhSa`$GnZ+Oli!8FpvdbxCCyu-hY#JmMpaVBe@wBktK}+_-UL#*Df4+G}eH55FTuLN7Eb zClSENdScdU`j4hfo8mM8Oh8tSvCNVOAAHcOBjZ3h5^ka2b=O^mk_A^>aRm)A({exr z8k=N3@Ep{r803ZQ5NwtNdwz?LD3VF!EBns*0^zF?1CKB#n34cc&C#W=z5O3g5#kbB)aS}pKpJtGHUvV-9W`Q z`pQf20G`EC7@uSV4TcbC9V4=^w>xz4m5Cm*+g9A;RXD@?MT-G-b7%sufI_x;f_qZv z$}6v=t%?N!77c1h)Ut%Dd+xa>>&+)wtoq$z9wZMq8jczaf;30xE{3xH!3Q7U3^$k( zmn5QGVM7^Pb2--oOVJ0BL zx8E)HPAVi223DCQOLSX`g@B^9_dD&h6R%7*Apup%R5)88XtIqP**AE2r4sc{pjHC` zI?7#7$gT3mP+5g4+ikak<<4XrbmS94%Wuo3g{&D6uF@+*ny^Gm4`PHI0^)~)JF0Umy%;4OPJMQS(Q?*PZ$}&(@ ztD5&x1O*jO-dnrRKKopM{q+c4-P$zlgs9FFiq)?`YdLygltiC4Z5k?Tp`cb;3_f#8 zQTxLxr)DpB=&BIW^`>0rql_+D9|&27tRWI?Z0e2la&S7Na;AkcSjtNsRp94hh#LY( z4=(9%p}2Q3WLwynqY=&%n$)mR&J z7b}>hlOzaCAz_|?88)sb3XuNECdeR1NsT?-@^t}jlZ3)u17YYffT)uU76HzrxH^LM z!sQ?q(^*Cyb<|Nu9(knNURPEzjVU-F*2)KOL=BKudf1?uN_RxYX#l(-{*>$NVzh4E+MYJV zR12l4E3~_=$w ze1#8;a03(K;lqauB7@AggMIC8)zY&DyLoGsmKhL9={ z+6{6#9w@TatD4ZMQzvsgWV%=A%3yv1`*#!dsq&AYM0#1AmNdV8lopdJ&@v03D3sfx zF3;AC^KkIs!CE}=<)moXin%FWDLOG7b42E?Sws6vaD*4q#0$GPkbbDzqR@MryS&xA zO`r9q{G*_yEQB6DmKJk^_o60Qd-v`wOs+t_RSVeKwQE;Gk^8B&EXywpxkomrv@`!a zJdvY>9~+hHT#II}&xl@^2i(d%f+T4Say8GjFUycANPD%TSF%6~EM_QSC+G6dNm&?G zG0o24D4F(G!zyZEx0WqiGTL!-Y$wvw(&C!LXH7(kxw&F^BZpj-6HU~l6&}(DL05HU z0D;3oxpMyb=hM#eXP&gS>36;7PqHwt4O)~um$K3ZTX=PI)EF!TA|eqEuNp$6lU*h;?pR zMsCr@yu~1?1jiL4zicazN+2z_d6_mk4ayB8ALXTQm93hzg)473tfF z8zjB70|q82TKLca*FYN}b9qK8&53gGcrjd+Q7gaAnFCZEy(o(VkFeL<#iJ z5<}7EYTXYsAf;Mt4#_Q(=y{JGJ&2dcPMtcnggHN!!4IXXDODPpnXJ^WviRjHIioaJD9DJJh?SoWs>7!0lelRb`}OOmKlWzAgb7%O zY011Q?_s2=kjlDy94%!y^^zDQu(r+5DW2CH6RwB|gM>ycu!}MwF8pzlbPNZ~)S%%# zn8D`m0GOnd3C<%R6k5(f)I-%v4HrPgMaw0#Z3x=xA7kvG)-qkh%ZDQ4bp2<3k91YB ziRWVY2NzwT5L&kJ49rcNHr4Dwl$GU=I&9KxVAYvto|!gP2UsPvC}WpmWVxBHx;qua zWT}iEu&mB!x_)N*^y$@yY08B$D&Y+SaLhweSj$gRNW=Ak0|(j^glV`mF6xV5$#im7 zZYLTnb5%ii+C%}Wnr)$$qjTrZ8oDyd-q0`kCzY#zKJp}>l)Zqh(v}HMD3qMepN__j z8&}hkN(kbdtD-BAOc!n|shC#`A#k{nyKV`Fpd&!z#*LL|!KwZZB4eb##>1(*6{B6d zcDB`sSABDnRWI?dMT-`4scdPKccjuDWpaMba|BA%4Pdwnk|=9ja(xAo-A!VIj~K!Y z8mA_)UME!IAsccTA?nI?L?B;LN&ahpA1Vf621?ajN5Du;O>*a2mERY+A*!(l-efyB zPEC1}CQWLT*j+%!Sml>@s8OTbCS1@7c{7j=5shuoYyDxIoUUhLb4fk2GY<^+A*=)UaX08Wczal!MH*Q?Pv?zF}jSh zGZY29r;ZeK_9h)8-45OO=^C%FUul>!Ym68%LPVN;Ob)aK=O}S8iv_>a|1Q)D?xFMPEQdih}9In>TMRg?vHLWkUf zV6E<)Ox2a4_|ul1rLVlI|H6`vf&|)~tVjhn0&Twa?x9*xfRTNqB-=X{1628M2m#fm z(vG@G({Qf5fmd6O@~6w(t2ybisU5R6zeP~P;9_&T<$uTIw!ukn7_DQeg+TCd3|qyK z-79Q??ih_Gy+qP}(NbI^6qfMJO4o*Q~vHY{!?5UX_Dy15OSb$w_ zxT0^Z2EWXQ0Rskf@7|q=vPMqms(2>b0Dv2aMO^aV) zcS7e?2CcBWW)GXIf0$=|CQuUZBY!63MwDTp1R#{E%;;GolUXzQ&m;d60U_O3hZaKv sEwFSNmF?glL1r=Rq<-cM88YPm0ApWkdR$X)7ytkO07*qoM6N<$f>{qsDF6Tf diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png deleted file mode 100644 index 46f559f65f11194c94c15bb7f195a1d72ef2a295..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX diff --git a/assets/icons/Interface/SmallArrowDown_4x7.png b/assets/icons/Interface/SmallArrowDown_4x7.png deleted file mode 100644 index 5c5252b167d2f9f9a1ce5e7b9f9c99123879c1b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8340 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 diff --git a/assets/icons/NFC/ArrowC_1_36x36.png b/assets/icons/NFC/ArrowC_1_36x36.png deleted file mode 100644 index 3a0c6dd0cb2b7cb6eebb1507fa68a8dbda11b146..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3692 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 diff --git a/assets/icons/NFC/Detailed_chip_17x13.png b/assets/icons/NFC/Detailed_chip_17x13.png deleted file mode 100644 index 9aaa1c5552a2d115b7042a4bee7a0c636ed00f01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 981 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 diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png index e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f..1783531285bed514517fc0821501e718359dd765 100644 GIT binary patch literal 1541 zcmaJ>eM}Q)7{3ZaLqs>76Ex1V5{uLJ`hl&zQLyxiR4J5Flue0VuU9zXKDZvVFu$in zjZPV|k*WB>lBo=wab{s;0v4vrHgq6yL6|@s=rj(*ZALe`w+OgD#xD2qyib0=-}CW4 z_wKW%tO^gC8wNp8xH$>4fiD6cy*LQG!v-3sLy!oej7F>3XoNYQby0aF1bI7aN-wPT zSzjw@m}hD^wN~8M!%5Suc^yp$% z;LAJvZZ<7U;$}9n&swkday_9{j$6k|(n<`UWz^qKx;6aE!&&NN(Msv}w)zef+FQ1w zPdX)Tbt_@>_j_ z1g4yN$-3nhg@rScIaE?HPo@{A*oop?Lg$pk$HB2)6bR6yfWuxok8z`3y<}7u1$MxV zNZ?V3kgJ!xNGj7}g^esv!dkgfMko{tSgVthPF&?syKrI|tWv0yh!WgdnNo|Y)TmMi zk6jWFkaxOJ8D8>lEl2;>9^cDOUul8V6b|{|}|<3A9_V zkuUT8Phq#ch$9gj>1GRf0_>e@Q6LnD8hH{ISl-UEdE_v6wo9ijB}kYxrRx(fq|eo5E&zRs*rRh@+=LHR*h1V=c1idZ;b1lJeL)dauJXW z64={+?e(||3{b$F7+$cL7=MxhGtYzJp6B{075o?>)?~ZM@Am^U<4XHBa7ryUV+Omo z^NSB}*9I*Vo3i}=cBw9McHOY`cJjO|@#%;8vhG*t42yEl!M7sbnHFlhs_u%cu{BLw zhaGH~)>1i`aAdfxzb-yDYp8axDf-aUrDb=dji35@J+L(5s-gM3W`<^_YQ(SZsXOZ| z+MW^|z2V5B)@@rgjw-Cq(dw%_ar$zq^bB!0SN!Wu`o1~8yPWDctj)SpoN(-94Cmev zzx{^qT>j%Hhc_onj|mkQqEx+SO=({jC(e}(-Mu#TV*2AZM;fP!-l&6{t3IlprLTNH zu7%|!`|gA}!?~;;lRx5Kde1<}Db&?0+p;8mU68o-2TkKA6-h@we_Yd@SR^HGPk zTw<~x)L!ghDi?=TUtd5z8164@t6%c>#|vtn#`%LIpusI7pJ(MpaIQm;*_49SCT!aE E4{AO|uK)l5 literal 4224 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 E4{AO|uK)l5 diff --git a/assets/icons/NFC/Tap_reader_36x38.png b/assets/icons/NFC/Tap_reader_36x38.png deleted file mode 100644 index 4e0ba8f05921ccbfbaa4390ad07e98fd454df00a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3748 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 delta 809 zcmV+^1J?ZR1=I$RIDZ2MNkl8%vq7p(O{Y^mj-MMz&IRWW8dlklb>M}APO&ha6JbE))xS3fLx{D2mT$PcjCsp6u75dCa(gJ zg$7PRSPT#B04M+ofC8WZC;$q80-yjW00cn+PyqIVdff}UEd^jN2tW-$4FI43C;)&0 zpa1|0fC2z00DlSqpa3WUfC8WZ01AKt04M+o=ok0&h4Aeu;6Acq%a`(~0jL3}0Z;%G z@Nf=r@h+O=aF-1JajVbpY;e=0xBM_mlE#+d@(I zXy`|>Bpb)H<`DiDYv&AilG_Q`jQyv4b+;EGjywBwI@!=Wze7mT)a63NR-^sKE^VoI z)R?ikoq$ccROX3yh8r`V&u!>kr%92RWrT-uFLDyYA=T)9ZL^C4M!sP{{#ip~od{G5 z!KOYGM1SiRGrZ}8zEHd`^&M`hr8p^-6^ch+P4@%l#%Nb* z_9w1klb8`Z29rAYwoV*V_sU}`x$^;k2=>c}On+r(1iqr(p}lcf8 z`1(t)=;A?jh;Tj}4!hm%BL%xZ_zo+ZoQUJ`xZm%$+imK*R977gDhrX7tlO~0!6c@x noFc(1Z$gauzl;~WM{(gdOXY*sI7{g=00000NkvXXu0mjfG$wR{ diff --git a/assets/icons/Passport/passport_DB_sfw.png b/assets/icons/Passport/passport_DB_sfw.png deleted file mode 100644 index 69b2ac9ad4568b6f1cdc9d38f2bedb6e5d71de6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 750 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 diff --git a/assets/icons/Passport/passport_bad2_46x49_sfw.png b/assets/icons/Passport/passport_bad_46x49.png similarity index 100% rename from assets/icons/Passport/passport_bad2_46x49_sfw.png rename to assets/icons/Passport/passport_bad_46x49.png diff --git a/assets/icons/Passport/passport_happy1_46x49_sfw.png b/assets/icons/Passport/passport_happy1_46x49_sfw.png deleted file mode 100644 index 56ea000cd03bf445ff64ac2a9c01318a4ed280e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1296 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 MT&7n)()IlKe@XVQIsgCw diff --git a/assets/icons/Passport/passport_okay3_46x49_sfw.png b/assets/icons/Passport/passport_okay3_46x49_sfw.png deleted file mode 100644 index e65da5b0e586ab706b40263b637f0fd3647ac5d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1304 zcmaJ=eQ4Zd7*FY#?Xc({wr<5?O#hgI`A#mEaJRF)-1W}xYPsFqxo)2Mc+-pRC25j+ z*P{+~I(14dBFvRBN2hbJDTpFM)qUugbz-}TilX~M+(1Ul>R)5DzSs8Z{wND1@AE!+ ze$VrJzFzO@Y;Rc8yaqv#hGarc!;^q7T~`agZ(r^0fX6yNmi0H=hF?@%fVAqi3eaRh z=>ch==!5+eAcP>bFPoXHpG|F&G`m136&s>da3C5%LXncAXuZHkRnTKvVQhN*IEI>f z7~9IHsFV{0c{5RV!RB&jMl1JfqK-v2qM?!m9Tb4Cprt~eP5w%}KSaz#yI=z@vg z8^#t%Wm8>f)OGRp)0DQsp_LBLoqZ-aU{**6eY2u#Bu1-gTZjFZb)f4 zz7!1J!dTw-9f>51#UfE;3ES-;YGU z4KZ4(U|7662dWrCHqsNW3A>{7$+k970G6MW!x;QS=%y|i6i|6y)o~Rt0?x6bhATY9 zCt(kQeA6&js6BX*w1Sa(rB7TZJgk#$$1&t%^-rp08LMsKz#7 z>?JIBN30Zef#Tb4#lx?BI(#Kl}VE zr>*Xj7oPuOCYk$s)Y#tsK*%~np8RyS?!L1%zI<}}!MWYx!_m5O^Oebw6XCJ(_ObK- zTzE2ZDn{m<55rd*dxnMr?}ze}_QjcOWc_;&d9i~N9qGpsZ_J!4w`@NTS~`yv?;ke? zhv>V?2d>v2-`~IQ&nd6F)duN|MblK>4PVZ`*vyPb!yT5yrS%35K zA7@kco=YdXx2-A%Zb=_}`+d*RzhdRBuUsVFjp#>D|NZ4t)}y$rkd*-G80G55pQ2+n{ diff --git a/assets/icons/Passport/passport_okay2_46x49_sfw.png b/assets/icons/Passport/passport_okay_46x49.png similarity index 100% rename from assets/icons/Passport/passport_okay2_46x49_sfw.png rename to assets/icons/Passport/passport_okay_46x49.png diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61.png b/assets/icons/RFID/RFIDDolphinReceive_97x61.png index 2528ebc95d79335975511a384c70c010d476a8c0..e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083 100644 GIT binary patch literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ literal 4862 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 diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png deleted file mode 100644 index e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61.png b/assets/icons/RFID/RFIDDolphinSend_97x61.png index fef503263fd962534e7e5543954612bf6c1faefd..380a970d9004cba5520560fd9aa24aa42924e2a1 100644 GIT binary patch literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ literal 4882 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 diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png deleted file mode 100644 index 380a970d9004cba5520560fd9aa24aa42924e2a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57.png index 78c4e93f8a8c4e94eb0513c7b59f6dde68748205..34199910945376f054daa0c1738d7e64dc410421 100644 GIT binary patch literal 2681 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 Ef`SCamH+?% diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png deleted file mode 100644 index 34199910945376f054daa0c1738d7e64dc410421..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2681 zcmcImeN+=y9!+ao5u{4RRaA5sx(ExC`N$-bj3$_n$VUkvvWQeyCX-2+CCP+jAc4io zTCG?Mt{#hG!Ga&HhzErAqZB!|3a)DvrMRvHWox_DA|h?~cy_C;?gRq5d#vj}n{y`f zW^&*C-FJU?-ehB1N_?RIEPs(m6quNxO&87<;ZXQJFMO|vU*0dACfO5~J4K>^Y2M>G z(a!3bBGF5=Y(^HJrB5bl&MKyioPiO$t#$z|5-p5%+bKGa;Q<3yE5Ey~* zc}h_2EeK@k(||b6!2mKb0?`P90fa(~%5YqU!~htAAuu9^Q4B(5B!ZJD0r)`AD7TI{p4cVOGV+>lxNjq3O z&vG`v%Saix0$vFUN=KJqwU5)CWtj)-|oKapyz6p$$;u$3aUm19L{!RP-!Ry`D_8IeE%PGl^OyD2NiXtdW#63}s*l z1!SZo6hupL8ZyWcDI_5lb-=g@OT!CeUm7-`bPIjoeBAJ$5l8Q5+!d($ki3w0A% zr_j10-}AAQ$@h&cEHDx}lA^s?SAw*+$&3;7-DaQQ-m~c(rFG>p0_jtlKMHelCf-Fk z7`0h&`hSKC{yFhZs_^O3pRMu#N9jIW>0HWYW`vCs2EB`cy<5y^Q{eyZ*Q0)qWkxNe z+1pL0&jt-;9ydhwaaFfD_;RXU5RbgQtag7C2 zhiG(KOrnS*)EX4kX*7sj5~q_a#t}rJi_^#+n>n(QQ9*%bx71r}g zOh?Nr0V%D+vkjR^-L8r$uG*D%=0_JstSCthwRNIZ6UzYqsFat{*<}NghoNb+7R7YN z^|kN%>y`Lk?$*NL`?8^w53Bq;UQ8~MR$yhHIfmNh{(!hK6YktHA^)}ZHQ^PW@~*Xfnv zyN6~6#V@*a?tRU-Y)Q5wKX~|}3?sI6m4^BC_BFJqEC)q%r`~OT$$YnRQ_CMZ2ENIB zSl)B!q*-I%{>kmC9VctbLQkruy?m74mCYagwq{Ri;J}Z7JbcyMg8I~c&%6ipv48H( zZR>5U8+0X?JC_bM#2pJOxZWB#J^jn?bJfX{FIk83{d9;cX7x>YYDnoE^-aUI8}RgZ zIBn9citKF!AINLkL^rrTGK91fqwf0=~Vsi8CK5AM0vh)#dnRC#A`T7`eDX;tPYB~dd1 zep=o%u`*1)tI*R@76D5xM*-QB#zTG=7j%O3(`FIAIS2uN{Aln&F@9)?Dgd} zcc|l&fZy&dy)n6`u6{#l>hr|5n%Cp|4{!DxI2g=S9N<1rj_Ukn(c0 zH*)b`%3H3s_r2=MII*qfW8LLsxV-L~J2pzaJNj2i-vtbLR7>;!-db4@Os~B@@7zkZ zXT=LO^0)h?M_*gE=d1MHA9+sji=XqXuRWFCB`NV>4_bG+XE!Au&4Dh5v>)+5xA%Q< zt!wkCD*1qFcbm2QU-Ns4j%yP)AGv=iHmi5LIIl#~eyjM6MEh_+V&0tvJ5=S=dJOTy~%6^nwZGp5xyf1Iy3Yx}id-~MjWmBzE@l0x=}tLFY`$&$eh z_Sk*5-$8w_6 zvp|`kS>timtI5s5`bs6(^a21c5|2dMSRj!gGKJ)Y_s0Q1NN<*1uyf*yxc=Y@PQcN) zXYNI+{}lkNK<91{bjcOG3t+Ab3LDl%M)5j|iV0<+9BQw2@uIS#Mskjnc^lo1Iuq@1 zPUojwf5EFuq0HIYjV~J&lbMs7EwomF2q9smgo#{VKo=dU2k()Kvqsf6Rz|Rkp@uj? zf)kAQ-duG6Fvmd)38y|c*kpS~0@wj^m)arW`r(~xV~a-v4Qloc zqCkTr@Z9bXPhKDi0Q_lT#$ezHA29RFLZ1uhDNO8_0D5u{6mkPGY(Ux(^F!>0Zvl6Q zD`*w=n)g5s-4LwCSyRjw;qqGDoIRqF3kZsJju$X=2O`ppRhj^h4m*H6DALRUvgF8t zwpR}ox{)KxP;5XABe^;CRJ(>~S@LJ;;Dx!N5&4n#{x6I%5=RBSc*ek)sTm3)s7VgX zmi+)Az1H?_TgM=Na$;`KvB>+i#8p)|a0j+^)F8&It&fZe{kmjgNP8kn7o= z2UhzPo&|ax0iVG&SB*ZNoHM+iL)W5dI-7SW2*rRPDnlH|&h~1ud zzEx%ewyLMSK_{5VHztn=PhD1@mrL2=Net!#=r^ouPQTv!is4@q{*#S8n%}uS2rvv^ zRJ|j;F98tr)>Nmh06=2(TAZOW8_=AZKMDX9-zCpIPLivaf;naK3uZA)Ocz3+&RULfT3A`<<3VCF zHQ5*@_aXM^2$31?^VbF6`(KTZH+QylMm|=YQ792E=XT<`b{+QqFgWqCb%|oA@LFw% z2mD36vl(6Kr~E!L<1d2$b-^OJ;6YT*pVIUn9vH%`lWV%uDp z-O2qV3V#2L1tBNM24Z)!P((8@U9mYs9LdO6&FlJ{j`zl|_&}zpe{d1{d_n0uGOPFj!5}QU6d-2ER}+!Sw7`00)CE+LL|uPaOoFo_Dn;yo$n$ zj`xQ4R`q(!Qf9?xr+b-!N=0lpH*Q?6ZZTOL{77ufnkzXLl>o8npew8TF( z&}P0w_?5RL@q^jTY?C_<)#TQWm-2_{VCS0RvETjf;mg|Qt`9#JV)&FN)cWMLS>zoq zQ~G(+dsHh#Lf?x{I^aRq{D$lI)5Cb+)%)^m^XtZ{jy5OxrF)gPm2^;>ni^F4={@wu znONPfOuuYQ)z^x?&6voCrkQmoqqU>!Q+iXv7+fh8So6o>&HbIMYTa0gh9~}(YT}SN0;n~y|9_h8Qz)nnLV?*%a~=wWx{f+ zY|{Z@+3pk66JZo;U{jDkxneqhSo1?+ZQ+M0CvO?=2LV}&S`v>=#WRFcgHk2z2nDwb zi1$Mo$>Y*L%iCXdEahdq&FC-sO6VMDLeyP)x?!c3A=Bvk0l_r-HMinCa-nH7t01G` z{bKwiQB^u*p}E)FHl!hy6Iys8{U<%^KpfD-kj+3eN74S zru+J%{joC{Wf5gh%C3amTs56f6{dhv`J#6Ha|;Fn$eWYy*+B9xotT$%-so&xo`(t;&tbwS7@qc2e)| zM3!Unvd+MXOUQMf8`frlA2oQa(aN+0sk?6!7Ofmjy1&s|zh3yNs+*LVl$nR-!>5!e ze}R@BDYf=eR!-y_qfHP#$t2$Vv@)i!7O>bS<{xROh>dhfI0(MEF3Nd9Za|cHG5>7xylkGN*m1B>(T}>P<@GQvA zI&FaHOzWquSZ=2Ix!Sr`dLvu24J4>PElb^28W+D!#<|W_kbi+!Jo*$%PZc4G5$56K ztzp!&v!rR<3S-tuubBGFYWhSQv*Yr*Z%tRgm(?$7uJ>t`%#SlNr9`EH17(WkORGW4 zvl79)KP~%RL*qi8ZC3Se{n)!M@Z)mH_6uAMVQAI8#=Z4}ivW|K=VtbH(oVBg6h0Xo&eon?)^smzZVD^1=E_xwIpjhTAraZR^%owTf=R%9g32agJ+;Os(A z+harhu)3aL1AUNQ2%KerfTLnSAq0P706YW%{?jg;CGRdnz@R@>sD23WU!2{qO$ba=6$*uG=)yHML4QABmOF~47aVP3 z_O~VnS4j0Sdn@nBW4i z6g-)VClNur7BL>AKq>;vqWV`11hSRYKZc0`e`ku7Ge`)A41uXZAq2v1V1H@{P|>*m zq4BTU0roU94uZx7kOC=K)`55_{06g<_un1uDzezXttqVQ0}vll+s?n?O?6F>+R)=B zFb$**3}%Wn)>7BjHPMFYXdsPE%(QiXV=agQR16V|`|TUg^8Gg!`CqYcBn5||k|_2h zlK<~Au<;>LNdZ12GDuxrO$&6y3WLQHcUO+?p3+}Io8Tz;Ae^Teg+u`TnPxctUnuWKDPU{wCBG_`5^SrDXDDWsPryOAx% z8Qb*4J4V#bug_E`4$vj4JhIE)WvY#0Z6=Ogt`Q3w6`1fCi~DkUb-dj(=?%y0vCrfm zJnG7Iw>w1ai@*ZiKV0Jrg(|w1gpw{4q0d<=?Kun`O-ejQ8A;&nJdlp?YKe)^eww~& z_*RZrH{)=4+|xO8-gQRoLv%;cVVY;>pTh)G02RCwC8*$2#?;~EF>_GxvQwYS<-QF~XTwM!|XY6giFkr+Wl zjKm6}A~Bl^lE#*Fpc18m)E29y_EsFUTCEP}_rCw{yFGcouXU1h&Ykq>^WM)r{?~Q= zuW{dBHGls6Uw--J#~**(WRpz}KKS6%Pd|Oss8Kz8_UzK7%Z@wlxaz8_Zn@=_g9Z)S zV~;($b?c_TYuBzmb#1n)s&tm0|Jb^B@4mtcE10s}a?6=Da^%PjHrQZ|HP+Z`uf6=- zZ@>LE+;GEVk3IJB#~=UeufP8M^Ur^^w`%d?#VfA3;>3v)zxd*d)mB^0#@~GN&4L9B zbi(xe@4s8xy1)JQn~{qaEiykqHSapR{Kw7UxpU_(S+c}Bem?l%gI+BD(n~M-Ic3U} zFTebfzCQTi1BxSwk(pRE{#DhZk3PEk>Z=0;-)EnFrjv+s=FBlvkF|dO`R6YGhXxJY z^`5W4{`%Kne|`V`_wzFvX_$=BUwiGf_19nD?^&~E{qVyNUJU*C8H2few!Kl_U-G_$}6v2G3$pPeh6HjK7D+iF=K{l zk#a$^>HPNFZ=-ym|0h@rxjk(+d-m-5SL~uudST?SzhQC6f`F}k1}O7M?1{zH*{MD< zK#LExw5r)cYr634RKkwTX1bP58z|p&lr`Q?|fA-ryBg~dy2n{K)(9xuQA^2jYL z^ytxJ?X}n5XrqnRTyxE1jyY!Fz=14l0L0Nti&BmE?A^O}Iw4_6o>6AuGGdDn6Z@u+4+iU~V&O7hC*=CywYFll!72K9R^w2|x3>lIr=Gg)? z=JLd@(j)qO_0?BY<`b-q3EzMJJ?v~v)?(<666^1{K^kSS1}(0)-g;C*k(Q!AMi3EK zS!ETmP~3$>yi73esLr~@_=$j6+@P`+lt^RU#NLjIY~g#yCft%ZfefE`B^_`(JsWSl zald~3IK%-59AKocm(_y>6E5NXPV*9^pM=_iDDv2Vem$C|f4y#a9tNl<2hi1RRv` z$y-qdrs~I9({&CWJeXPaAO_PHpxIaEGsVlk`|jJ4M=HYU!T=yz7}L@at^|}$O=e?D z!(lZc7BWub6J7!;{_&(F+jZAnmtHA}^|_s|q6Eha;A$EB{%=6H1?3 z%w5DyzX4WfGS7~>gn)0-89-imdddiG*Ijr0NHx}0OxE;>|w)(fs#zN-FDl7E(ya; zH{L-^Jf{du@G@e=2r<>fAg&XvksE9PuQ}kRrYedo7Kr({veW#Qsz~v3P0>&ba42P z!ZAf&uzDJ1LL(D2-#g|*j>xIei@+ke1!a*ZpM3JEW&{2D>#w)qZoBO!AOOlZED5?? zFmK*G?@Sc>K_5~fE=ZN3Azwl$-taYaW;ojb>S>y%`F^sX_m<2a2d50%Oq(`sr=519 zi5FgYK^z@7Zro*;U3S9_H}HCu*VL&~FTeb9F+5f5r=NcM#v5-ydHCUn%T@MGeZ&F8 zufJ8WxV@#wAP_Wtn=LwKyV0XZQvgM>h55K&he&@J3Fy1;zKeo4-gu*3k2>n8^Upv3 znrp7P>#n;57n$?sn{Vbo)O^7O7udyiRuk|%gnmtK15amO78nSIfWJa^xH_oPXalsoKOc$`v0JoDm2eLk~S< zBWn&FI@BVdTyn`J0ua!*-g>K|OxU{f&O7xd$?dbxK6GiXw)!E&PvXfbr?^gH?Ad3Z zeZ>`5AU^hxUOXd%epWU8GD&adKOZeDqJRQXfG}v29Cahg&%0 zf9fDFM;>`3Hlyje=bp=a^~4svcmZ9F2Os+|u642|^pNC4p*2nvVC8)1L;N&36BH&8 zDR`gb2tDn+_ug2~SgFtqW?389=8;Dp5gcH3ewAsCIN}KVp_iaT3-;@@a#aNB#9+c( zhe7PifgHOjg|fOeTf@g$CvHV~{MTk7oV{Op<&|x>-S&hNPCzxTdh*F9D{TnF;?xWQ z$0QZg!+pmccjRED-=WktT<5K~-fB4?*BkB%Ok(P_*Ip}lSqfZ2lB#}cr~rz6^I`)- zlMY;*|+7GhO6YW4Bs$3O7E17pXIeeAKvZomC@JP`+$cH)U@ z%)$rT5nIh+Q$W0T1mWTt%RU?%W}_5o@h9u6B}AoC)V5jNkMMF~v)PpqJsUZ`YQ%i?!9DlfgXWwO zFYzXUH>o_U3mmMUpXFGRn6ivk4mz@Bp3dA*!UG8vaJ$~kR;5TT1v79lx}alY`hbZR z_uqeiG{67;`zKGHEC{DEFh2Q)H7tVD^>c!LzPPjlU8*Owrgt@w=R5Db!;I7c16`z; zY(ohX+mSwsOosUU^Urn0=z0*va@OKZVmnr_BTvd^Q3*3~@Omh=W|DQMD50kdq*pIT zL9f30s=0bP3f4$76eF;ugo`P;i@j;FaTczsR1&;c!WC}tlCPw)668|M^0QpQ6<9@p zr8V+Xf2hgT#G@RK=#r**&%*XrrJS)x;bpAv;F2Ss0VxE-2TAX&v(94o6mKF!vMsIW zKCB*PS}uTETn7bVijXsB&XjxO43=U^&pGX_su#(W?{f&D^7G84D4~O@=!h& zl_Xo{>{hpF^i)TdAW*-FFh%FR_uh-dQbWvLr%mHRi5LC`ykP3c$XuXh@sJ^Co`3%NoWy%JTkH_`YRixap8MIH{8qe8qpcUmb%D3335|N0GO(mk!H(1;$>NIVL1pK!}I zv2||J3Q(}5Y$oE)L0}`czV5o~;(5^yf2gO1z^!ZvXs$jIiG(4@Ovs5b1!ak0EGvLp ziy>?oH7m5#b?Ta2l$lko`0E@^YE}Lv8gS|hFTBupifhCpGhIlM#z1Uw#+lu0RSp?~ zwuQ1T1i44Z8JwP|p~#1kY)zN^%vz~;d_C>7)5s&;!^#3VMdjF@5?h0V7l%0t`>U?H z3Ztk7*KGu-Db`O#k-99caRWmWI1wutZ4jXop@hjy!<(W@Qsz<_%G`qH#G?#V94SK; zqbHttBEsQfivQ>`2P{7kR7rpyR)bPjBuOyD8S0MMu()1AY`^{X^~($~mK7|K^9J3p zQBp#}gwW$F9pnO6KcEg0!qbQmBX-zf2WL(vyD?+NTzTb{3M;8UZV^R}8a0Z=%wkj8 z#i-=J=t67(I;mUOZ}@moVLXG-79y(^jlcJHL#ertc3y!}-iO0E zWZbxMx7~Id@;T!=13B-qvNNtol-NSM0|yRFm_r_Lt?4A4D}wIrt*YNzfl@B$4OKue zJtYyEI$0#cxQKNVvtmT}R0;XY=);B$qh=-dph1IZD=ebUDO096^*Uo1mh*m&K6c@e zrh`6UzyPFgz51)?v>eKUlG^DK1#@Z0y?n~w8Ej11dUCmCv)t^Ynuc3d03xYEB+F(^ z-ji#~a`6{Cm=ZUz5h0l_x4`q-Ta{8bc8^1qzh=^zOlY!g;_OTrDM3`l<>EPi|4JYO zA_LH4=+L3LeSGn{Cy;4a)#wk#TK+n zuGdQPdwj!kOOrpmiGN52UnUi=Dir~gn^;vk zls?7HQ#aJhZ_iZn33KF0F&c<02$rRA7p+CB3`=Pjf`X3pDKugGH#)l2YM(OSO8@`> M07*qoM6N<$g8AuxBLDyZ diff --git a/assets/icons/Settings/Cry_dolph_55x52_sfw.png b/assets/icons/Settings/Cry_dolph_55x52_sfw.png deleted file mode 100644 index 86d9db1b497cd9e49bb62987cb35075b4bc7a410..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3898 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 T?I80e8v&Rb+u+JDu3`TM+ZHm< diff --git a/assets/icons/StatusBar/Attention_5x8.png b/assets/icons/StatusBar/Attention_5x8.png deleted file mode 100644 index 137d4c4d054227f27ad6a7e3e374b100d975d9af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1690 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 diff --git a/assets/icons/SubGhz/Scanning_123x52.png b/assets/icons/SubGhz/Scanning_123x52.png index a48c5330e85c2135866fe99bcb6f3534d06c6d53..ec785948d035b717944bc41811f73d190e6fc3bd 100644 GIT binary patch literal 1690 zcmaJ?X;2eq7>)>*yJ)S*p|h?~Q6$-f1d>2NNH}5%Lt|z{KrzQc(y-YyStJQ+g^C9Q zSWv-YttjHfh@wyt@GLFhMTjB}M;LSv6%;{^;@J%X_DAW??0&~Q&+|U-`@P@n?x@Hx zJ8Nfa0)b%14d?LjF%^HQmS*_Zy;Prz4^CJ}G`0p!z*2-Nm=GjEMKHicgo!X87D}`~ zG{XJ_!fZF0AR3G2MKHxELKK=XL=B?E*#v@rphhVa%V7)_o@3{?OoMWF~y##kV3^-~Ura#~iQo~#pIF_K28B$0`bDW@qQkN5vj1er#wF+Tj+ z?|%xb1zIIc;=^h*StZ6#E@7!Dl#l0+R;G}k zDeC1D1RjscRj4tcLJV^`ED)C<%48Czw=bJb<4}SjatMt~4q?;jbe~|z8=_EsXfzI; zGR5Vf;$#F?U{hSlXD)k2uBjOiB_5drt7MyCNvH}%fQg)$vYEXwX4ISHN@n&FG$WUU zn<1G__FpGGwS~8jX*%7w_+q;CVFljrD!j4V;}c)t_r;dW2@+`9`eU1O>Hy1H=Z_x? z#)vXQ4`HsunYK6jxY4-M*|zlR`rg;$+V*St8!R`}`J(H(M(Fn4S4n;$pnW-UN$QA` z?fY?KM*pGHedF?8-QC`CtG>sHp2-iZetB?^`cj>4f5g#9o;N06J$3^rWaoX6Zp!iN6AS8ml(v%micb;q2O2KuEfM&;;GfOLnbEbQ;Xh2MB~uUb$O z=Tf34Z;Mngv^s8}xvK?|Q)X5xaeMf&yLz=D);7(;s_;xKaAkeDy_0K?%Yn2|k9C6T z^kdg4x7}!2l%F~e-2N&yKK{I*<bm{PN1M%~EqIS+ z#{g%nih7mt;>v=&E{mA(e4W(c;<>|5`bM6gWBHY@vRSt9LE+^Uhns6zS!2UzZw(cU zoUfWE)n;PLGTwze=xNtR#E5s54 z?u4@P3{ljfm#4cr?==i}&Q~nx+6o_SALMl>7g+8+b*EGr+g0b>QusCdoqSG@XTkT} zJKW>*p7M7!ScsC+I$SWe`PeH**g7LKd+2^e4BT{9(i5Fx*_t#~^L&3q*^<`Bg8SLL zJr^&8Wvz+nlWyNPFyz7qEt>zJmbmY7f19~I|Kz}R|I(kU_SSdG*|X+`TiP_=YR)O9 z%>w2`t#*GaxqGd${KiVYrAK$bi$_|DyT^Fr>F$>+;8w9Tw&Z$d+!FWSfdT)vdK%bz zZxc14Zjp1X%kW^EaVRy?Y1r_3XHB46)J>z~?!08J+0&8}hNJx?^62efuWTu{t@zFL zVZ4jin2pbcGJ}2f!HjR`wC}%p=#A$7DVOR4RjdwPz~ZWrAMHE3V&KiltB9(rl{Yu) zpLzA}yq2xqYW6IxKLj-^F^5Q^3s;lX5 N!3~Mzlm%~0{|8*@s)qmo literal 4092 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 N!3~Mzlm%~0{|8*@s)qmo diff --git a/assets/icons/U2F/Auth_62x31.png b/assets/icons/U2F/Auth_62x31.png index dd220bb65104666cb19752a96fe716cc44dffb68..40f094ac9ba758ea06838c2bb8376cf6f28f8050 100644 GIT binary patch literal 3761 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 delta 1860 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 diff --git a/assets/icons/U2F/Connect_me_62x31.png b/assets/icons/U2F/Connect_me_62x31.png index 495e8ab55c1e2dabb547151c2319f321f4af13bb..68c48c0e68142548919d6a4b02e40b48a243b04e 100644 GIT binary patch literal 3767 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 diff --git a/assets/icons/U2F/Connect_me_62x31_sfw.png b/assets/icons/U2F/Connect_me_62x31_sfw.png deleted file mode 100644 index 68c48c0e68142548919d6a4b02e40b48a243b04e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3767 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 IM6N<$g6Xe=bpQYW diff --git a/assets/icons/U2F/Connected_62x31_sfw.png b/assets/icons/U2F/Connected_62x31_sfw.png deleted file mode 100644 index eeaf660b12ea4647154c8d616b468a3098203356..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3765 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 diff --git a/assets/icons/U2F/Error_62x31_sfw.png b/assets/icons/U2F/Error_62x31_sfw.png deleted file mode 100644 index bb280e75121e12eae045d87a33f61b1713af6f85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3751 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 literal 6876 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 W#^eyOwOvyH0000ZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj diff --git a/assets/icons/iButton/DolphinNice_96x59.png b/assets/icons/iButton/DolphinNice_96x59.png index 43cc58bd9b263ddf1ae1676f997ebd3aa5c2d74d..a299d3630239b4486e249cc501872bed5996df3b 100644 GIT binary patch literal 2459 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 Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 diff --git a/assets/icons/iButton/DolphinNice_96x59_sfw.png b/assets/icons/iButton/DolphinNice_96x59_sfw.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QmC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S literal 5122 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 diff --git a/assets/icons/iButton/DolphinWait_61x59_sfw.png b/assets/icons/iButton/DolphinWait_61x59_sfw.png deleted file mode 100644 index 423e079199b00df0d910981caf8944cbaf8ee67e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S diff --git a/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png index 90b589ff8e9f18b28d7fd75ca525a1db24381b06..2b4bec7c6f14f53e7362da75f95b83bf387eee54 100644 GIT binary patch literal 2157 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 diff --git a/assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png b/assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png deleted file mode 100644 index 2b4bec7c6f14f53e7362da75f95b83bf387eee54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2157 zcmbVO2~ZPP7>*~02PorFsdimdMA+fKow8|q?(QRKr!r*R@tj{x`6Nwm6rph(oMKb4%yr|RP{ zg0_fp1D#2V?H0xj7ln_vGdPh=@<1k;MOjtg(}RaWfHJ7SsWLsHXEdaVigvJMk|REu zaAXro12}#h5N^i=0t?CGfZbxYa+qBOw(?@a+`SBgKr4jLR)K1_Kp<700BC5I1mt1_ zA`k=x6iTr~E|toWFaSkR1V&`A1cfAW43T0I1<-zhf;84(#1gep?XrX~6=>pl27_Un z%_g>u7Sn7NEKw?zFoMD;3JC~^%eV5l9kOyk9SmBMBUp;zDcTCS8SzXymsf#;rfnuz z7!R$LYj>02FxZYWutbcwO=<-i2oH|QWzDU^4FpV@NegM^IRPv2Uu!HmLMbZ1c^Z%iZLddr#Tb-4|aIAJ=QRoh9z;HW|L{! z+!3gR4i*5Fh*4nVRLW|gZCr?3O8Ws)i}R!k6rv`95LCF6Q4~XDm`oljK`;bqgX)Dm zFyK7?-@vqiGUk5}Y9KHp&0285OOyrAB4Ngw)hbP|$6~A;k6Q^cMymn^RmBu#z~oX= zAyX=h5GuSNqe6;6nNlJXl8sgv38P$rAcVBzyp|?%-4X0KZ}^|*C$W@JLAd$jc{~xq zG_;v!^|V3o@@NqFb3I0*NnmLsWfnHLMBM}+CQ>7pDCKep6-(TS-kNY&G{p%~&2KNA zBr>OcW~PAF9K&$JT?Q(UaL1oCfbGlFN4v0%)@C9F(tpW|HW)`6c^l4>>MX(CAIv*g zP#$&{Y?~eM-%V`Y`%7_mz=e+Co_bo9@Zo88q*dr}tkBAQ5}G1KqRww)wCZGg`K{GA zoW}w0;vDp8%bD|$0VhRZaP^aU-_`NZ!(iWfs5*t&z8tl<RcBM(Lo_&0F%-q_ojos_KXD@w|bUY$` z@QA0blA+dA6CC92eP`Xy9k&B+7hX;`pS<>9#gIE(Swzdl8>=K~&gP(t_vgm1`L!;8 z`^clyDwm$UhGqwx`u;g}Uw$_L==$Qd1JuOH4+&d9*ORIU%T@ z*7ixc6E>y4yb*XcZeHUkzZ`yBL~KUNLf@5?>cW;jFV8r?O6d+l_u>xJ28WJI^14{G zIlXJ*L!ZV0nydbX!MZ(922~t>VLDtDH+kjVqnRaP>g(G6Iay!^Pc3_PJ?+Kx(yxI<^BY^Q)!X-B-}(oy5N+XFBB^Sf z?vbpf%vIRpd+-af*PHbPft%M(tK2P$U2vT650vE%Yr0hNHl(P`VRLppvTRXb*|emG zvuEu8@_f_8mL2=@!urCeyHep%6}v;S`zZz&d*AsX>QmS2)wT9l(;MgHFIzgjsv_tF zzBjPum)d=fnViot)zA~qi)SfqibKE6xBu?HwuaTsysTWeNb7iayFpw4bvfdO9l@s z__)F1Bs4IC%YIQH0Rc>WAo4yCJ%`2*B+|!?rVl@O_`&A_2d#}Vq4$T(!SHS{c%BRZ zO#&yRJiMU!FAf0qZvz*)Y{BsXz(M9f_Wv8q3@|zHKR6J0U@VHp4_^cv_CWM!pd5we zfCtGv0rw0KmjEAYvj@6tMdJ_$x8NRu0gi+6!2pVb>i&Nzh(D+P2pB$v;~)4(`~dg= zz$g#|c-#T?fPx>#fDgDla1=fydxORPGhi6`AmH-;gV4Spq5>d%?e6vX1Ke&Qp=XeO z0OC&&`2*+xGo^Vzz~%l2lnxDH1dxzH@gMNP&twmo|A!9^1&}^3_8bHxAAk4`0)7i9 z5Db&X4=4aXMJ5kS06kkK02nYn1*QNLxo`pM@gRFE%pODp2bF*x!~_DA!2YBJC5(On z5C^dU$zwf$MhWcmJ_q$ICiWO50{&GKdyEeOV@JRMej>5(P+RZ-7`SMEs6+e`%m{D~ Og3&$z^CF>$-U|nYD9Y9V literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm new file mode 100644 index 0000000000000000000000000000000000000000..843aed27c7df836480f795cd649be62ae08f6ad7 GIT binary patch literal 462 zcmV;<0Wtmo0LlRXAM^hM2j_qv0g(b1{{QfUgO`ENzlTmAfCsnZ-#-zY1_2(x1JnQa zpU5x);RFmIj^FtF!@x2kY<}bOkKlNqkOKjVL_Smh@bq91ZU9h)#t$9%xiAHCfP4Yv z4+w}p2f`0C(u2r+VDcXj0EDD2FnHX}OCATj#2%sY^Mlc{!Sdh((UQROC#Zfw>RuDt z1IfA|^(CAEUSRdWLFPsE2dEfebKv`bf$6}5#{v1^gO~3?8ra`UB*?AfosK+`caK2f!Rd;?FR7|AFNLCzHIu;ty~D#(+b>2gW@gg~5r^p_{(+8!2 zeqix8mI*jc?Ad%Ohh0Y E96sC7hyVZp literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm new file mode 100644 index 0000000000000000000000000000000000000000..ad87a307189f29491e693d9665e9c9780584f1ab GIT binary patch literal 487 zcmV={XPjp2dR8PL=T9&y+VlZIXEbZ<-iBL z44zYPDBT$Ej}Ouzy-{_a2$C2Pi7B@|AP+=2i*PsFpzrS0qC$N7=!Kt4?_8H z0qQw0eV0rgo&b7_Odgg7d4t8`N%fc~%pO0|0hYuHAolr?43;7Rj1SiiKrncLMi>uR z|M~(C83+C$0K)^$2minR1_dzw1qA+p@&E8lC2+h0#QuQt-~It*I8U%Vzwje){3LGn d2b!qLWdJ{zJg?x9RE-24Ux0h3MTmq0fq*|E+=BoB literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm new file mode 100644 index 0000000000000000000000000000000000000000..78de0389efdf70991b33b41f855675bea8b3248f GIT binary patch literal 483 zcmV<90UZ7T0N()sAH)9#2j&C+0|d8o{sb@o|Dqpm2R{BSIQ#;94`1h=egq%zw5Nl1 z`^VxO0P#Ss1$O<%~y{_3%OC!1`{OEX-yP zQhHzvXZ#1O5I$T0`GeekTmbzvOdkMpz(+y(V1P|Q_Jk-!DKs!{!ec`g{ZOUl37v0q-|=uK*tKF!>Y7h=BD4 z2z-I`02$B>f$@h=hycOnUN{Uqek8L8#DBquhJ)7t4@>=r01w51G|mhUhzLCc<-iB2 zB*FIiFnV|a>wgEmfgWJ-0SGv-2r`%h*YZFz*scaJFJBn|$zmW7dqc0&^`H0=&I6WuBKln$G z=wxsXF};rBq8>R^h+^+CK)`>)vBQJs|5bqAA@F1=C(s^Wa7e&m#NmIaJb^<%HH>&3 ZO7jPmd=ycL!Vv-Jj~oCzVGTnElpL}U-NOI? literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm new file mode 100644 index 0000000000000000000000000000000000000000..32bb893cae287bcce9d3d5d3590bc1cd890ac96d GIT binary patch literal 488 zcmV%HfP*6h0v~`pVBx3d-@t?Z0gd<`-w!SG~&;P*%1`-&!f#u&QiD1B{cp!M5 z*T4@k@dHIRya0Lsf#>%$s64y?;s=b)P_gmygT^usf5(U(kdXWvj2uS;QYP-^ZX0iFb`n)vCn&d56B#B5Dl;X{tu)0%s^rfiTZqiV*vP%+!iA71(1FT zLqLEd%kUq6`Tarm&olf3#r`v3c_qXiqwzP4iGD!$i@VewpWt~w!{k3M06t0LA1nYq zU?TGm%s#^afdk484pY=(jldH;hC4F}W*|6#yFMeBeEtp@Uy@Oi7wz`rnfxJVwz^9PcMUFR@>OabfjRUhO21sK5naO49W30Ohx5C1Zf{{Y4- z)Bj)m3WXF@cn1(3zw@}jcpeg|cmeDz9RD01DFH{IJg}hw!|=pnzfgI2!9xJSnG7D^ e_#h}?$3+LG{sBOMK_QAm2b3Hhun0grhzAGqspMh+ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt similarity index 93% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt index 6503043bd..fb0fdb228 100644 --- a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt +++ b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt @@ -1,6 +1,5 @@ Filetype: Flipper Animation Version: 1 - Width: 128 Height: 64 Passive frames: 21 @@ -11,4 +10,4 @@ Frame rate: 5 Duration: 360 Active cooldown: 7 -Bubble slots: 0 +Bubble slots: 0 \ No newline at end of file diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_50.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_50.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_50.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_50.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_51.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_51.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_51.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_51.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_52.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_52.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_52.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_52.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_53.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_53.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_53.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_53.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_54.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_54.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_54.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_54.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_55.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_55.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_55.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_55.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_56.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_56.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_56.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_56.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_57.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_57.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_57.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_57.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_58.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_58.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_58.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_58.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_59.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_59.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_59.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_59.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_50.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_50.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_50.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_50.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_51.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_51.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_51.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_51.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt similarity index 92% rename from assets/resources/dolphin/nsfw/lvl_7/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt index 43dd6af5a..f169de719 100644 --- a/assets/resources/dolphin/nsfw/lvl_7/meta.txt +++ b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt @@ -11,4 +11,4 @@ Frame rate: 6 Duration: 360 Active cooldown: 0 -Bubble slots: 0 +Bubble slots: 0 \ No newline at end of file diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/meta.txt diff --git a/assets/resources/dolphin/nsfw/manifest.txt b/assets/resources/dolphin_custom/NSFW/Anims/manifest.txt similarity index 100% rename from assets/resources/dolphin/nsfw/manifest.txt rename to assets/resources/dolphin_custom/NSFW/Anims/manifest.txt diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L*8cZ?uu=dgz6zc1zuF%k=stJ-|M&0z-3QJ0`%nLm?|L6C z_`Ze%@BfSh0WzEIf9J~&*aN^G0PqKZJOTJ{e?$L({j2zc{Qw8-k1O;C=!g zFd6`S;As%56qNx{OcsI=LJ)lDVh~9drbG{zJrf{eFYrDx@dzzbB|rzv4h0~9!6IMK zd{yZXnq-E6z?d8dK_J9Jf%6l}K%qk+XiW|QNT5_8LHUH~U{F{tqwqhN1fmg;fqa4S z0m)#nl1hMnWV8uILV<$#2j(k*g+L`fpnOGW5=HpX1LFY#h><99C`=X;CGg+aCGQ8E zM?pZMP$(Wv_^_+Wb@k zK|s-R0pdSs`~xUy7*|NY5&cXq5TXXf*w If>Zeg7;o0wEC2ui literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta new file mode 100644 index 0000000000000000000000000000000000000000..485a421b6e4585f9e7e41e4cc628e815074fdb04 GIT binary patch literal 16 ScmZo*U|?_nVs;=_0Ac_O$N=a7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx b/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx new file mode 100644 index 0000000000000000000000000000000000000000..4ab6aef41bfadab1eefbfc91a98cb0a2f4022f8a GIT binary patch literal 480 zcmV<60U!Q=0000$000010Mr2hcmu#50PqKZEB^xrWT2jv0*4hQsbACd?J@E?;1 z1<)Un2nFCDx(RqcV}lU~5=q5DCZP77w@&A|B)s zeL(S$I0b+oP$)1EW5D`{1IN(^2hRe12cQrHA76+b0DvI+*Fd-c{EZt%0tYYn5Woe1 zP6#~w5DV~uprG*=lMrwWMxg>l0#Lw734~I37#>0#1rm|Kg(<%WJXgUkpx!eEU?2Qk<{N5=r5Kr z2v8Vd5@{5I9tJ3!ifIW06hC{I0%#mLlg~<0u@(_O97=whUF!(&@kgRKk6h@3_O1aE}9J(1`hS%#>POy z-Q00TXaBSg;sx*U0@hy X-4EY3s0JCQga3)P0fzCy|Kj-|K{|=G literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx new file mode 100644 index 0000000000000000000000000000000000000000..9dc1e5dcf52f7721ffe6f002905111825fcda8c9 GIT binary patch literal 513 zcmXBGT}V@50LJm>eb0N&&YXj@wdq<)XCKB~5p$#$7P-|{+sv4;XvvDx=wj|7L?{S? zj=9R3Nr^%3na+9@>m$&mErIrq%OKhit56PKw(XK;Lql==>?x%R23Ol z_~>Oh)a@+MvyHN9R@1r-y9aG3GqF>mkw6yHmn2!$Qb;t}K-<4z#w$XddxVvG8??+i zG8R$OK6zlX*iFO5K1lY0N;=~(*WLwbi^DhGz&}l-;bsSd27MfIt$Rn_pdiY!!sk?4 zw-8EvX}Pz=288kc(B?BndB!jA9ug=YMy9zAC{bl8wvbusbS zO+u7rHbK`Qf0daOyXu6wnNAE5Rh^Y-%bgg+Xb*j|$eHm*B6IgyAiU2^)^bgbM#V;+ zmxN@iXPESi8n-&)Hqts$(`S|P@IJ70Tl{0JYef#|lX>Cj!+>_YF2@$8g9MM?3xVDN zTCl#OT8z})IJvA-3uM1Cc8h8?T>rV>5l%4*BcppFTwI@Wy8S1(zaM_YqPs%8aELi> t8{fhUX>fy1fgniNxeJ&S#V{idsF0~dO*nPBe7?N;id8D}o7d+nx__kHlc)dy literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..03c6304a2d4d334ac519d1861981fcfd0393755f GIT binary patch literal 375 zcmV--0f_!D0000z00000K&2#AN(I0uRnb%ia8*gFk_?zCl}e=wSXHT1RT->OB_&ld zR;pB~N^O=Zsj5SS@@HBmk^dq#ytRuT=U801$K+RRa+K zaBk`XfJD#@t&ac@bT?`dNCaG8ts??}|8-Bt0AK-Iwap-1KyNLN3kvvJTMfkluQs+y zf8bSZT@3)JYBg&Q33{qpo=gDDv^1*@K&q%}tqKUlS~XjZ05DW-t`0!zvs5b$60j*f Vt&RWy*=k!20Kl|ft_px;u4-Myk9+_C literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx new file mode 100644 index 0000000000000000000000000000000000000000..8af359b940e9a14587bb8ae598aeff8e53b6542a GIT binary patch literal 286 zcmZo*U|?_nVnzlb#%uNaA8<5O*ng-ku`!k4;5o#|^Znq_;{W{zl;wX&{8nT5x4|L7 z;m-p%=id#Nn*Ycj`mg?t;r@Z|{|si^i{!~gt0>JR^u|M0*4X8pte z@-PmH43zU<{sYtF!*BnvZ>zC8@B^gpzg+Y5@3#B``oC=IV+w5de*eLCI8b`#gFl5I zc76PB_{U{&aMyu~V@doza((q1rsO$4e#qW&=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx new file mode 100644 index 0000000000000000000000000000000000000000..715815f0cad52a4138cb8ff3db6a9fa15e4a2b46 GIT binary patch literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx new file mode 100644 index 0000000000000000000000000000000000000000..715815f0cad52a4138cb8ff3db6a9fa15e4a2b46 GIT binary patch literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..cb92216c4e5943d4d845c979c7d8c59df040b95b GIT binary patch literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUyG$`6BHT)5{U3WANw^bz!@%V0w)Q;`Zx7& zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H7W%wg literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..8932c5ed3ba499c5a9259d2d228330e5f4a5e94d GIT binary patch literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUkjyb6BHT)5{U3WAN#c`z!@%V0w)Q;`ZxA( zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H5-7Y~ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx new file mode 100644 index 0000000000000000000000000000000000000000..3eefd86bc994996073b05eee2f627dba8b8bb9cf GIT binary patch literal 502 zcmV z&sG8a-`8rskx5wK7)>Us0pP#(ZleJPjN~>tnxu#E->Y)$fYOw}IF(JzlkvaqZm59L zn!q(3O%<2%+Ur?tK+={#D4LqLCjC;k%6NwJ4USW(=8s@H*J~&=g(F0i>akI}1lB2G z29#7bHL8bJFJPO-b|BIzjS`7K#=-O%tW1C!M1j;(FcPR7!&yw2Ez*HV7Yqg=LaI~& zsicZPB~S=J3}pb+q)`E64ImJP(n$co14jWCf(_P~1R!X_pR1rP93p`O2CV>wAkqb> zY=+_ussN<{uuL&bhVa-R0*C`%KnXG~hT{Pe2H0%?W&lvRkz@%4I0nT)8g&3`(G6!V z8{X0a4S?BdfsX>z14;}HcpX8aX?KFr3IhRwgI)=cf_y4aB`d}%kV=M}r1S}a;HwN6 zph_Sw)HbgIK>`NU0#X~Lki}qD0BlSk6KD-E)SclLKKZ5N}US)0ty8g z!LX4heZasK>JQHga$BQC_!{%pQ?BqK(GqQC;$Ke literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..22a76dd4daaa01bda8be01f522bf58c0a9e22e2c GIT binary patch literal 352 zcmV-m0iXUi0000q0000108|0&f4~O;{CmL10@edpx%UGcAJ`2@FYpYIKkykK|6myQ zf+Gh8mv8h2PuJ7`4+<8n3?S3C?LY9)xc~3I5AZbZzE3Z@uhZZ zPz|IH-D|$@AQ%W1@>|%TEd~Sqk?t@M3`7RqN1OqM5dr>prx*}vUuXA#(BdE+%j{qb zJV*!k?{NbU02)8(xETMR+sLGR5f1m>b_O75{_c1R1wsS7|IC4-*noew_dE>((E+}D z?)X>*aF{>2oEibnfL}lNd*lNi4Ih2i@q*UCKO5^mgT4(f{NG9cqrktL+dc#TmVtgh y_WcKc4on-}yc#iZpo2$&-=jf4AA9x#`V=qyv>Gt@C-d+B;D17bMIPUS{uB^jDyGK( literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..75f57367cf539f9f075e06c983de83f6fb826cbd GIT binary patch literal 458 zcmV;*0X6=60000q000010KNgL>v~p-wblX;ingkga@+Z!VEF3Vy1h-Nsss;>WlLjT z+vSDxD$@V=`)#Pcbv>>9|7#Wx&5Nt|-|OO_wXk3SV@c)v1LO}`1~3LaECxm|c7+Gq z2m>6h2L%U~gW5raz`%6S3p+Y|APS-Q zK##A$oB+fgJ^=WG-rxfrlnVe4j0c1gI%d$oTd#n@lgCjD5;hNsL<$U^9wc}l9f%YX0SK{8!Osw=LDNGFxR2stpn?O& z4e&3=BFhB=4Gy_F7sql89~ci(C=ZJS76saX%cTJLz+kXFCR literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..4a569c1eb27507a1301fa34d2ec5d96d62fe43be GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tYz`Jw>Mf_uuaow2G_xpSDq; zYo|#3>Y%M&TBU-sCA_pIg(RtNzcnQ&bm13Kd)b9kJiK12zMi3FI8`f)^MACLpQdN3 z=)KufA{GS|X?~AhIooSW3e$h-uWP0ReU-W&JV|S%%1@Q@+Zy4Mync!Pn|*w;)=D4c zdgFhqTqaEuyBGh*d!?2x_y6g;CVMUVBl<7?uj(X`k4pPP|E*rK%8T>={%uROCQi`; E05YU{Gynhq literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..582247d43e0c1cfbed6e9d8061ec26b7ad417339 GIT binary patch literal 257 zcmV+c0sj6z0000V00000`~UwxNs=T9{r~^Jk|aqI|Ns9#RaI3L|Nr-YBuSDa|3CGA zNs=T9|NrKHRaJcz|G&$Bk|b3r{eRPaB}r}t|NW(FNR?D7|G(LLtEB!2y?!8TBq@^> zm;X<{N>ZjGd;TjjQKhOBU#n8JN{Y%Qn;1$el2nokx7w6aRgu;xKOLczNUDk@>zFDr zl@k97|658w@~{~PtJl!ST;-&2w*DXEkcciSjeQYtwq z{+q{>ic(bte>487R7FxM-e3Gnk*Jg;|ChRxDiyT~{$KqiQleB8zg7OLL{*t0|G&1S HDoL^wQy6*i literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..fae05bb57f5b883c6fd532a150899d556ce750cb GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tY!bdWlT`@4w$GXd+kjKW(GH zrK?5aR|jqN(oz+iE#aj#sl;1#`>iQKp$q?U*~>1R;<0L)$m91?1y!xebKX{VX$}fjR%WrFhPjdRI@^AL>$y%W& zx$2Got#T;|61*4x$9v_Y{aXL0@0#pY8p-l6{;%q!9YLD=L;tN-37x|AfB&|nt0q}# F0ssSEetQ4_ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..c91714b3f1114d842e45cb614c456d3b40b164c5 GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCV9C983fU@}>T=Wn`|tM(TCZ3APupl> zSB!Z4>Y%NwLUw7-mhjS=wCD@#_FGeeR7JmP*~>1SvSP(2(bqG)LRD3FD*un3G9|<- zj{n|lrO3`H>-4@yubmaNQe5`Gw2+pT>uRa{!ArwDwNy3BZ*xRXTH+z}Px|`gRY0xv zoBwHf3VCwfi~r+2X~`sw|I>E`FV$Kq^e_IOs;1`@mHkuyczXqD0^PYUbmf#FP5?Ek BcEJDu literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx new file mode 100644 index 0000000000000000000000000000000000000000..c37cd533d7a3df21e738cf4cbffb4483f9a551ca GIT binary patch literal 684 zcmV;d0#p5S0000!000010H6YZLN^}%?5|1(7!WzTpQ}}`Euew}ngP5Hj;~c2fP$jGr)v<6eb8Gs)sl8j*#s@XScl6&=tNY&k92~!j|6}!C%F({S z=Im!~n^}Lh8}|n{qCf2YTIbN;d^{Y!$luc2+tfmT;O6p2{@$+s;u0Nyq&ubU+S+43 z;Opc1T&ewrC;o+lm+g+}cjy1GaL2*R;^lwxR{!*0y&eZI%bknDfA}9@>-@Qxxdguh zHt$7&%lhSFp*QRvm%JTsk2Vzmw?QSo;B$U?kf={G8LRhAgRRMeLZOk0*ZO`PU~tAW zGY*xyZVq0LW(g9^P&}V`@Hu-mpY+pX;Q!sljoGOGruHw-|JDv}PtX5f!E=B2vD)AP z#ix>f|Mpw>b$Pyj<-U{q@850V)#iKul=@fOKerC90~!AOL;9bN{p=jrZnvfZu;g-g zRp4^vK9Gnff+LLY|G~?N_C_Fh2_GAK{tmaJ;{kwixP9R3K|27^fI>VQ;B!Z_5HK-V zHR8sKP65mN2PXnGet`jtLErm~$U0E(#1IT!4Zr*xvG4R83}26Ub!ZUZ{*Qr+{oe;HgCGB&fs6m!4z_>( ztu6=ux*dOd0K6ane|-FK3V^VG;DP*q{h;O83ka4!;OO^3$=7KuaoHhW1k0s;3Y7vSDM*2jpjMLZBI+Z4XW{hX{Mk{Be^YK@J5q) zMh*$0wO$8l8=yrnoQmn700Rn)+GZo12Eue7;L~08>hhygRXatT! z5V%2MQ|#Y9&oc{*PGg~rLZ;cyZI~7~7?py-2L+C779>Hy$m|%5R4_(dd5!}iVzNvi zFsbB!9t$iDlR;&G(+wrSHI4(FQq1J_fU>v($AQ|K>7?1j8saoG??ro}3r+_Dp#c8y z@RePXLVLP>;p~ z1<-;%L;z7qL!HGRAvc6clAzh^h+u>z+PQZ&$1fdia9P4Fe%-@6W<@^19;PE{V0Oz>C%GhgJ z`+8s{;n_2uD_3<3AA+l6e#e32@NT8_TotcCe!)e;kJ<6k^Z%#h207tugoa(l243*s|ZbVAbYge|i#h@7nr; zp9<{WL}jZ7*6J%9UFd64^bSU=JrHKUlHPcbinq^Xl%^-b<_#RJ^*~y(=ahV36X1F# McGibf>8G#$2UW|uRR910 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..47b611a6ad2b3c2fa21cda23b155e56742d70c77 GIT binary patch literal 482 zcmV<80UiEq0000q000010M-Gj>v~p-wblX+YPPDAa@+Z!VAkr|y1h-Nsss&fWlLjT z+vSBXD$@V=`)#PD^*ycq|7#Wvy^E{&-|OP07o_t2fwBg_O97FK-Jw9&%HVKNd1y8? z&PSZtNcj%R8vp bytes: return data +def pack_anim(src: pathlib.Path, dst: pathlib.Path): + if not (src / "meta.txt").is_file(): + return + dst.mkdir(parents=True, exist_ok=True) + for frame in src.iterdir(): + if not frame.is_file(): + continue + if frame.name == "meta.txt": + shutil.copyfile(src / "meta.txt", dst / "meta.txt") + continue + elif frame.name.startswith("frame_"): + (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) + + +def pack_icon_animated(src: pathlib.Path, dst: pathlib.Path): + if not (src / "frame_rate").is_file(): + return + dst.mkdir(parents=True, exist_ok=True) + frame_count = 0 + frame_rate = None + size = None + for frame in src.iterdir(): + if not frame.is_file(): + continue + if frame.name == "frame_rate": + frame_rate = int((src / "frame_rate").read_text()) + continue + elif frame.name.startswith("frame_"): + frame_count += 1 + if not size: + size = Image.open(frame).size + (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) + (dst / "meta").write_bytes(struct.pack("