Merge branch 'dev' of https://github.com/ClaraCrazy/Flipper-Xtreme into detect_reader_uid_fix

This commit is contained in:
Willy-JL
2023-05-13 16:23:28 +01:00
627 changed files with 19823 additions and 6714 deletions
+26 -21
View File
@@ -12,37 +12,37 @@ if __name__ == "__main__":
event = json.load(f)
release = requests.get(
event["repository"]["releases_url"].rsplit("{/")[0] + "latest",
event["repository"]["releases_url"].rsplit("{/")[0] + "/latest",
headers={
"Accept": "application/vnd.github.v3+json",
"Authorization": f"token {os.environ['GITHUB_TOKEN']}"
}
).json()
artifacts = (
os.environ['ARTIFACT_TGZ'],
os.environ['ARTIFACT_ZIP']
)
artifacts = {
os.environ['ARTIFACT_TGZ']: "application/gzip",
os.environ['ARTIFACT_ZIP']: "application/zip"
}
for asset in release["assets"]:
if asset["name"] in artifacts:
req = requests.delete(
asset["url"],
headers={
"Accept": "application/vnd.github.v3+json",
"Authorization": f"token {os.environ['GITHUB_TOKEN']}"
}
)
if not req.ok:
print(f"{req.url = }\n{req.status_code = }\n{req.content = }")
sys.exit(1)
req = requests.delete(
asset["url"],
headers={
"Accept": "application/vnd.github.v3+json",
"Authorization": f"token {os.environ['GITHUB_TOKEN']}"
}
)
if not req.ok:
print(f"{req.url = }\n{req.status_code = }\n{req.content = }")
sys.exit(1)
for artifact in artifacts:
for artifact, mediatype in artifacts.items():
req = requests.post(
release["upload_url"].rsplit("{?", 1)[0],
headers={
"Accept": "application/vnd.github.v3+json",
"Authorization": f"token {os.environ['GITHUB_TOKEN']}"
"Authorization": f"token {os.environ['GITHUB_TOKEN']}",
"Content-Type": mediatype
},
params={
"name": artifact
@@ -59,14 +59,19 @@ if __name__ == "__main__":
body = release["body"]
body = re.sub(
r"(https://lab\.flipper\.net/\?url=).*?(&channel=XFW-Updater&version=" + os.environ['VERSION_TAG'] + r")",
r"\1" + os.environ['ARTIFACT_WEB'] + r"\2",
r"(https://lab\.flipper\.net/\?url=).*?(&channel=XFW-Updater&version=)[A-Za-z0-9_-]+",
r"\1" + os.environ['ARTIFACT_WEB'] + r"\2" + os.environ['VERSION_TAG'],
body
)
body = re.sub(
r"(https://github\.com/ClaraCrazy/Flipper-Xtreme/releases/download/[A-Za-z0-9_-]+?/)[A-Za-z0-9_-]+",
r"\1" + os.environ['VERSION_TAG'],
body
)
body = body.replace("<!--- <HOTFIXES>\n", "")
body = body.replace("\n<HOTFIXES> -->", "")
insert = body.find("\n [//]: <NEXT_HOTFIX>\n")
body = body[:insert] + hotfix + body[:insert]
body = body[:insert] + hotfix + body[insert:]
req = requests.patch(
release["url"],
+1 -1
View File
@@ -5,7 +5,7 @@ if __name__ == "__main__":
client = nextcloud_client.Client(os.environ["NC_HOST"])
client.login(os.environ["NC_USER"], os.environ["NC_PASS"])
file = os.environ["NC_FILE"]
path = os.environ["NC_PATH"] + file
path = os.environ["NC_PATH"] + "/" + file
try:
client.delete(path)
except Exception:
+2
View File
@@ -55,6 +55,8 @@ jobs:
- name: "Upload hotfix"
run: python .github/workflow_data/hotfix.py
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: "Merge pull request"
uses: "pascalgn/automerge-action@v0.15.6"
+1
View File
@@ -2,6 +2,7 @@
*.swp
*.swo
*.gdb_history
*.old
# LSP
+9 -3
View File
@@ -1,9 +1,6 @@
[submodule "lib/mlib"]
path = lib/mlib
url = https://github.com/P-p-H-d/mlib.git
[submodule "lib/STM32CubeWB"]
path = lib/STM32CubeWB
url = https://github.com/Flipper-Zero/STM32CubeWB.git
[submodule "lib/littlefs"]
path = lib/littlefs
url = https://github.com/littlefs-project/littlefs.git
@@ -34,3 +31,12 @@
[submodule "lib/heatshrink"]
path = lib/heatshrink
url = https://github.com/flipperdevices/heatshrink.git
[submodule "lib/st_cmsis_device_wb"]
path = lib/stm32wb_cmsis
url = https://github.com/STMicroelectronics/cmsis_device_wb
[submodule "lib/stm32wbxx_hal_driver"]
path = lib/stm32wb_hal
url = https://github.com/STMicroelectronics/stm32wbxx_hal_driver
[submodule "lib/stm32wb_copro"]
path = lib/stm32wb_copro
url = https://github.com/flipperdevices/stm32wb_copro.git
+1 -1
View File
@@ -1 +1 @@
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/xtreme -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/xtreme -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap
+22 -25
View File
@@ -11,11 +11,10 @@
"args": {
"useSingleResult": true,
"env": {
"PATH": "${workspaceFolder};${env:PATH}",
"FBT_QUIET": 1
"PATH": "${workspaceFolder}${command:extension.commandvariable.envListSep}${env:PATH}"
},
"command": "fbt get_blackmagic",
"description": "Get Blackmagic device",
"command": "fbt -s get_blackmagic",
"description": "Get Blackmagic device"
}
}
],
@@ -28,20 +27,21 @@
"type": "cortex-debug",
"servertype": "openocd",
"device": "stlink",
"svdFile": "./debug/STM32WB55_CM4.svd",
"svdFile": "./scripts/debug/STM32WB55_CM4.svd",
// If you're debugging early in the boot process, before OS scheduler is running,
// you have to comment out the following line.
"rtos": "FreeRTOS",
"configFiles": [
"interface/stlink.cfg",
"./debug/stm32wbx.cfg",
"./scripts/debug/stm32wbx.cfg",
],
"postAttachCommands": [
"source scripts/debug/flipperversion.py",
"fw-version",
// "compare-sections",
"source debug/flipperapps.py",
"source scripts/debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps",
// "source debug/FreeRTOS/FreeRTOS.py",
// "svd_load debug/STM32WB55_CM4.svd"
// "source scripts/debug/FreeRTOS/FreeRTOS.py",
]
// "showDevDebugOutput": "raw",
},
@@ -53,14 +53,16 @@
"type": "cortex-debug",
"servertype": "external",
"gdbTarget": "${input:BLACKMAGIC}",
"svdFile": "./debug/STM32WB55_CM4.svd",
"svdFile": "./scripts/debug/STM32WB55_CM4.svd",
"rtos": "FreeRTOS",
"postAttachCommands": [
"monitor swdp_scan",
"attach 1",
"set confirm off",
"set mem inaccessible-by-default off",
"source debug/flipperapps.py",
"source scripts/debug/flipperversion.py",
"fw-version",
"source scripts/debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps",
// "compare-sections",
]
@@ -75,10 +77,12 @@
"servertype": "jlink",
"interface": "swd",
"device": "STM32WB55RG",
"svdFile": "./debug/STM32WB55_CM4.svd",
"svdFile": "./scripts/debug/STM32WB55_CM4.svd",
"rtos": "FreeRTOS",
"postAttachCommands": [
"source debug/flipperapps.py",
"source scripts/debug/flipperversion.py",
"fw-version",
"source scripts/debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps",
]
// "showDevDebugOutput": "raw",
@@ -91,27 +95,20 @@
"type": "cortex-debug",
"servertype": "openocd",
"device": "cmsis-dap",
"svdFile": "./debug/STM32WB55_CM4.svd",
"svdFile": "./scripts/debug/STM32WB55_CM4.svd",
"rtos": "FreeRTOS",
"configFiles": [
"interface/cmsis-dap.cfg",
"./debug/stm32wbx.cfg",
"./scripts/debug/stm32wbx.cfg",
],
"postAttachCommands": [
"source debug/flipperapps.py",
"source scripts/debug/flipperversion.py",
"fw-version",
"source scripts/debug/flipperapps.py",
"fap-set-debug-elf-root build/latest/.extapps",
],
// "showDevDebugOutput": "raw",
},
{
"name": "fbt debug",
"type": "python",
"request": "launch",
"program": "./lib/scons/scripts/scons.py",
"args": [
"plugin_dist"
]
},
{
"name": "python debug",
"type": "python",
+3 -2
View File
@@ -8,11 +8,12 @@
"amiralizadeh9480.cpp-helper",
"marus25.cortex-debug",
"zxh404.vscode-proto3",
"augustocdias.tasks-shell-input"
"augustocdias.tasks-shell-input",
"rioj7.command-variable"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
"twxs.cmake",
"ms-vscode.cmake-tools"
]
}
}
+20 -2
View File
@@ -239,19 +239,31 @@ distenv.PhonyTarget(
)
# Debug alien elf
debug_other_opts = [
"-ex",
"source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py",
# "-ex",
# "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py",
"-ex",
"source ${FBT_DEBUG_DIR}/flipperversion.py",
"-ex",
"fw-version",
]
distenv.PhonyTarget(
"debug_other",
"${GDBPYCOM}",
GDBOPTS="${GDBOPTS_BASE}",
GDBREMOTE="${OPENOCD_GDB_PIPE}",
GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ',
GDBPYOPTS=debug_other_opts,
)
distenv.PhonyTarget(
"debug_other_blackmagic",
"${GDBPYCOM}",
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
GDBREMOTE="$${BLACKMAGIC_ADDR}",
GDBREMOTE="${BLACKMAGIC_ADDR}",
GDBPYOPTS=debug_other_opts,
)
@@ -335,3 +347,9 @@ vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*"))
distenv.Precious(vscode_dist)
distenv.NoClean(vscode_dist)
distenv.Alias("vscode_dist", vscode_dist)
# Configure shell with build tools
distenv.PhonyTarget(
"env",
"@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)",
)
+1 -1
View File
@@ -23,7 +23,7 @@ void AccessorApp::run(void) {
exit = switch_to_previous_scene();
}
}
};
}
scenes[current_scene]->on_exit(this);
@@ -17,7 +17,7 @@ static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val)
canvas_draw_box(canvas, x - 4, y + 16, 24, 6);
canvas_set_color(canvas, ColorBlack);
canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val);
};
}
static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) {
char emote[20] = {};
@@ -85,7 +85,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) {
canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote);
canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header);
canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value);
};
}
static void battery_info_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
@@ -1,10 +1,12 @@
#include <furi.h>
#include <furi_hal.h>
#include <stm32wbxx_ll_tim.h>
#include <storage/storage.h>
#include <lib/flipper_format/flipper_format.h>
#include <lib/nfc/protocols/nfca.h>
#include <lib/nfc/helpers/mf_classic_dict.h>
#include <lib/digital_signal/digital_signal.h>
#include <lib/pulse_reader/pulse_reader.h>
#include <lib/nfc/nfc_device.h>
#include <lib/nfc/helpers/nfc_generators.h>
@@ -179,6 +181,153 @@ MU_TEST(nfc_digital_signal_test) {
"NFC long digital signal test failed\r\n");
}
static bool nfc_test_pulse_reader_toggle(
uint32_t usec_low,
uint32_t usec_high,
uint32_t period_count,
uint32_t tolerance) {
furi_assert(nfc_test);
bool success = false;
uint32_t pulses = 0;
const GpioPin* gpio_in = &gpio_ext_pa6;
const GpioPin* gpio_out = &gpio_ext_pa7;
PulseReader* reader = NULL;
do {
reader = pulse_reader_alloc(gpio_in, 512);
if(!reader) {
FURI_LOG_E(TAG, "failed to allocate pulse reader");
break;
}
/* use TIM1 to create a specific number of pulses with defined duty cycle
but first set the IO to high, so the low/high pulse can get detected */
furi_hal_gpio_init(gpio_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(gpio_out, true);
LL_TIM_DeInit(TIM1);
LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetRepetitionCounter(TIM1, 0);
LL_TIM_SetClockDivision(TIM1, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_DisableARRPreload(TIM1);
LL_TIM_OC_DisablePreload(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM2);
LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH);
LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N);
LL_TIM_EnableAllOutputs(TIM1);
/* now calculate the TIM1 period and compare values */
uint32_t freq_div = 64 * (usec_low + usec_high);
uint32_t prescaler = freq_div / 0x10000LU;
uint32_t period = freq_div / (prescaler + 1);
uint32_t compare = 64 * usec_low / (prescaler + 1);
LL_TIM_SetPrescaler(TIM1, prescaler);
LL_TIM_SetAutoReload(TIM1, period - 1);
LL_TIM_SetCounter(TIM1, period - 1);
LL_TIM_OC_SetCompareCH1(TIM1, compare);
/* timer is ready to launch, now start the pulse reader */
pulse_reader_set_timebase(reader, PulseReaderUnitMicrosecond);
pulse_reader_start(reader);
/* and quickly enable and switch over the GPIO to the generated signal */
LL_TIM_EnableCounter(TIM1);
furi_hal_gpio_init_ex(
gpio_out, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedVeryHigh, GpioAltFn1TIM1);
/* now it's time to parse the pulses received by the reader */
uint32_t timer_pulses = period_count;
uint32_t prev_cnt = 0;
while(timer_pulses > 0) {
/* whenever the counter gets reset, we went through a full period */
uint32_t cur_cnt = LL_TIM_GetCounter(TIM1);
if(cur_cnt < prev_cnt) {
timer_pulses--;
}
prev_cnt = cur_cnt;
}
/* quickly halt the counter to keep a static signal */
LL_TIM_DisableCounter(TIM1);
do {
/* as all edges were sampled asynchronously, the timeout can be zero */
uint32_t length = pulse_reader_receive(reader, 0);
/* in the last pulse, we expect a "no edge" return value. if seen that, test succeeded. */
if(pulses > period_count * 2) {
if(length != PULSE_READER_NO_EDGE) {
FURI_LOG_E(
TAG,
"last pulse expected to be PULSE_READER_NO_EDGE, but was %lu.",
length);
break;
}
success = true;
break;
}
/* else we shall never see "no edge" or "lost edge" */
if(length == PULSE_READER_NO_EDGE) {
FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_NO_EDGE", pulses);
break;
}
if(length == PULSE_READER_LOST_EDGE) {
FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_LOST_EDGE", pulses);
break;
}
if(pulses > 0) {
/* throw away the first pulse, which is the 1->0 from the first start and will be irrelevant for our test */
bool phase = ((pulses - 1) % 2) == 1;
uint32_t expected = phase ? usec_high : usec_low;
uint32_t deviation = abs((int32_t)length - (int32_t)expected);
if(deviation > tolerance) {
FURI_LOG_E(
TAG,
"%lu. pulse expected %lu, but pulse was %lu.",
pulses,
expected,
length);
break;
}
}
pulses++;
} while(true);
} while(false);
if(reader != NULL) {
pulse_reader_stop(reader);
pulse_reader_free(reader);
}
LL_TIM_DeInit(TIM1);
furi_hal_gpio_init_simple(gpio_in, GpioModeAnalog);
furi_hal_gpio_init_simple(gpio_out, GpioModeAnalog);
return success;
}
MU_TEST(nfc_pulse_reader_test) {
mu_assert(nfc_test_pulse_reader_toggle(1500, 2500, 50, 10), "1 ms signal failed\r\n");
mu_assert(nfc_test_pulse_reader_toggle(10000, 10000, 10, 10), "10 ms signal failed\r\n");
mu_assert(nfc_test_pulse_reader_toggle(100000, 100000, 5, 50), "100 ms signal failed\r\n");
mu_assert(nfc_test_pulse_reader_toggle(100, 900, 50, 10), "1 ms asymmetric signal failed\r\n");
mu_assert(
nfc_test_pulse_reader_toggle(3333, 6666, 10, 10), "10 ms asymmetric signal failed\r\n");
mu_assert(
nfc_test_pulse_reader_toggle(25000, 75000, 5, 10), "100 ms asymmetric signal failed\r\n");
}
MU_TEST(mf_classic_dict_test) {
MfClassicDict* instance = NULL;
uint64_t key = 0;
@@ -513,6 +662,7 @@ MU_TEST(mf_classic_4k_7b_file_test) {
MU_TEST_SUITE(nfc) {
nfc_test_alloc();
MU_RUN_TEST(nfc_pulse_reader_test);
MU_RUN_TEST(nfca_file_test);
MU_RUN_TEST(mf_mini_file_test);
MU_RUN_TEST(mf_classic_1k_4b_file_test);
@@ -407,7 +407,7 @@ MU_TEST(subghz_decoder_ido_test) {
"Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n");
}
MU_TEST(subghz_decoder_keelog_test) {
MU_TEST(subghz_decoder_keeloq_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/doorhan_raw.sub"), SUBGHZ_PROTOCOL_KEELOQ_NAME),
@@ -676,7 +676,7 @@ MU_TEST(subghz_encoder_nice_flo_test) {
"Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n");
}
MU_TEST(subghz_encoder_keelog_test) {
MU_TEST(subghz_encoder_keeloq_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")),
"Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n");
@@ -813,7 +813,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_gate_tx_test);
MU_RUN_TEST(subghz_decoder_hormann_hsm_test);
MU_RUN_TEST(subghz_decoder_ido_test);
MU_RUN_TEST(subghz_decoder_keelog_test);
MU_RUN_TEST(subghz_decoder_keeloq_test);
MU_RUN_TEST(subghz_decoder_kia_seed_test);
MU_RUN_TEST(subghz_decoder_nero_radio_test);
MU_RUN_TEST(subghz_decoder_nero_sketch_test);
@@ -852,7 +852,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_came_twee_test);
MU_RUN_TEST(subghz_encoder_gate_tx_test);
MU_RUN_TEST(subghz_encoder_nice_flo_test);
MU_RUN_TEST(subghz_encoder_keelog_test);
MU_RUN_TEST(subghz_encoder_keeloq_test);
MU_RUN_TEST(subghz_encoder_linear_test);
MU_RUN_TEST(subghz_encoder_linear_delta3_test);
MU_RUN_TEST(subghz_encoder_megacode_test);
+1 -1
View File
@@ -34,4 +34,4 @@ private:
} // namespace cardboard
#endif // CARDBOARD_SDK_UTIL_MATRIX4X4_H_
#endif // CARDBOARD_SDK_UTIL_MATRIX_4X4_H_
+1 -1
View File
@@ -8,8 +8,8 @@ App(
stack_size=8 * 1024,
order=50,
fap_icon="appicon.png",
fap_icon_assets="assets", # Image assets to compile for this application
fap_category="Games",
fap_icon_assets="assets", # Image assets to compile for this application
fap_description="An implementation of the classic arcade game Asteroids",
fap_author="antirez, SimplyMinimal",
fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Asteroids",
+9 -9
View File
@@ -174,7 +174,7 @@ static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) {
while((furi_get_tick() - starttime) < 30) {
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) {
break;
};
}
}
}
}
@@ -357,7 +357,7 @@ uint8_t avr_isp_read_lock_byte(AvrIsp* instance) {
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE);
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) {
break;
};
}
data = 0x00;
}
return data;
@@ -377,7 +377,7 @@ bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) {
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) {
ret = true;
break;
};
}
}
}
return ret;
@@ -392,7 +392,7 @@ uint8_t avr_isp_read_fuse_low(AvrIsp* instance) {
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW);
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) {
break;
};
}
data = 0x00;
}
return data;
@@ -412,7 +412,7 @@ bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) {
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) {
ret = true;
break;
};
}
}
}
return ret;
@@ -427,7 +427,7 @@ uint8_t avr_isp_read_fuse_high(AvrIsp* instance) {
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH);
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) {
break;
};
}
data = 0x00;
}
return data;
@@ -447,7 +447,7 @@ bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) {
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) {
ret = true;
break;
};
}
}
}
return ret;
@@ -462,7 +462,7 @@ uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) {
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED);
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) {
break;
};
}
data = 0x00;
}
return data;
@@ -482,7 +482,7 @@ bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) {
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) {
ret = true;
break;
};
}
}
}
return ret;
@@ -111,7 +111,7 @@ static uint8_t avr_isp_prog_getch(AvrIspProg* instance) {
uint8_t data[1] = {0};
while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) {
if(instance->exit) break;
};
}
return data[0];
}
@@ -348,7 +348,7 @@ static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t dat
while((furi_get_tick() - starttime) < 30) {
if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) {
break;
};
}
}
}
}
+2 -2
View File
@@ -98,7 +98,7 @@ const char* get_error_code_name(ErrorCode error_code) {
return "OK";
default:
return "Unknown Code";
};
}
}
const char* get_error_code_message(ErrorCode error_code) {
@@ -121,5 +121,5 @@ const char* get_error_code_message(ErrorCode error_code) {
return "OK";
default:
return "Could not read barcode data";
};
}
}
+22
View File
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2023 лень
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+14
View File
@@ -0,0 +1,14 @@
App(
appid="bomberduck",
name="Bomberduck",
apptype=FlipperAppType.EXTERNAL,
entry_point="bomberduck_app",
requires=[
"gui",
],
stack_size=1 * 1024,
order=90,
fap_icon="bomb.png",
fap_category="Games",
fap_icon_assets="assets",
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

+643
View File
@@ -0,0 +1,643 @@
#include <stdio.h>
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <gui/canvas_i.h>
#include "bomberduck_icons.h"
int max(int a, int b) {
return (a > b) ? a : b;
}
int min(int a, int b) {
return (a < b) ? a : b;
}
#define WorldSizeX 12
#define WorldSizeY 6
#define BombRange 1
typedef struct {
FuriMutex* mutex;
} BomberState;
typedef struct {
int row;
int col;
} Cell;
typedef struct {
Cell cells[WorldSizeY * WorldSizeX];
int front;
int rear;
} Queue;
void enqueue(Queue* q, Cell c) {
q->cells[q->rear] = c;
q->rear++;
}
Cell dequeue(Queue* q) {
Cell c = q->cells[q->front];
q->front++;
return c;
}
bool is_empty(Queue* q) {
return q->front == q->rear;
}
typedef struct {
int x;
int y;
int planted;
} Bomb;
typedef struct {
int x;
int y;
bool side;
} Player;
typedef struct {
int x;
int y;
int last;
bool side;
int level;
} Enemy;
typedef struct {
int matrix[WorldSizeY][WorldSizeX];
Player* player;
bool running;
int level;
Enemy enemies[10];
int enemies_count;
Bomb bombs[100];
int bombs_count;
int endx;
int endy;
} World;
Player player = {0, 0, 1};
World world = {{{0}}, &player, 1, 0, {}, 0, {}, 0, 0, 0};
bool vibration = false;
void init() {
player.x = 1;
player.y = 1;
world.endx = 4 + rand() % 8;
world.endy = rand() % 6;
for(int i = 0; i < WorldSizeY; i++) {
for(int j = 0; j < WorldSizeX; j++) {
world.matrix[i][j] = rand() % 3;
}
}
world.running = 1;
world.bombs_count = 0;
vibration = false;
for(int j = max(0, player.y - BombRange); j < min(WorldSizeY, player.y + BombRange + 1); j++) {
world.matrix[j][player.x] = 0;
}
for(int j = max(0, player.x - BombRange); j < min(WorldSizeX, player.x + BombRange + 1); j++) {
world.matrix[player.y][j] = 0;
}
world.enemies_count = 0;
for(int j = 0; j < rand() % 4 + world.level / 5; j++) {
Enemy enemy;
enemy.x = 4 + rand() % 7;
enemy.y = rand() % 6;
enemy.last = 0;
enemy.side = 1;
enemy.level = 0;
world.enemies[j] = enemy;
world.enemies_count++;
for(int m = max(0, world.enemies[j].y - BombRange);
m < min(WorldSizeY, world.enemies[j].y + BombRange + 1);
m++) {
world.matrix[m][world.enemies[j].x] = 0;
}
for(int m = max(0, world.enemies[j].x - BombRange);
m < min(WorldSizeX, world.enemies[j].x + BombRange + 1);
m++) {
world.matrix[world.enemies[j].y][m] = 0;
}
}
world.matrix[world.endy][world.endx] = 1;
}
const NotificationSequence end = {
&message_vibro_on,
&message_note_ds4,
&message_delay_10,
&message_sound_off,
&message_delay_10,
&message_note_ds4,
&message_delay_10,
&message_sound_off,
&message_delay_10,
&message_note_ds4,
&message_delay_10,
&message_sound_off,
&message_delay_10,
&message_vibro_off,
NULL,
};
static const NotificationSequence bomb2 = {
&message_vibro_on,
&message_delay_25,
&message_vibro_off,
NULL,
};
static const NotificationSequence bomb_explore = {
&message_vibro_on,
&message_delay_50,
&message_vibro_off,
NULL,
};
static const NotificationSequence vibr1 = {
&message_vibro_on,
&message_delay_10,
&message_vibro_off,
&message_delay_10,
&message_vibro_on,
&message_delay_10,
&message_vibro_off,
&message_delay_10,
NULL,
};
void intToStr(int num, char* str) {
int i = 0, sign = 0;
if(num < 0) {
num = -num;
sign = 1;
}
do {
str[i++] = num % 10 + '0';
num /= 10;
} while(num > 0);
if(sign) {
str[i++] = '-';
}
str[i] = '\0';
// Reverse the string
int j, len = i;
char temp;
for(j = 0; j < len / 2; j++) {
temp = str[j];
str[j] = str[len - j - 1];
str[len - j - 1] = temp;
}
}
bool BFS() {
// Initialize visited array and queue
int visited[WorldSizeY][WorldSizeX] = {0};
Queue q = {.front = 0, .rear = 0};
// Mark the starting cell as visited and enqueue it
visited[world.player->y][world.player->x] = 1;
Cell startCell = {.row = world.player->y, .col = world.player->x};
enqueue(&q, startCell);
// Traverse the field
while(!is_empty(&q)) {
// Dequeue a cell from the queue
Cell currentCell = dequeue(&q);
// Check if the current cell is the destination cell
if(currentCell.row == world.endy && currentCell.col == world.endx) {
return true;
}
// Check the neighboring cells
for(int rowOffset = -1; rowOffset <= 1; rowOffset++) {
for(int colOffset = -1; colOffset <= 1; colOffset++) {
// Skip diagonals and the current cell
if(rowOffset == 0 && colOffset == 0) {
continue;
}
if(rowOffset != 0 && colOffset != 0) {
continue;
}
// Calculate the row and column of the neighboring cell
int neighborRow = currentCell.row + rowOffset;
int neighborCol = currentCell.col + colOffset;
// Skip out-of-bounds cells and already visited cells
if(neighborRow < 0 || neighborRow >= WorldSizeY || neighborCol < 0 ||
neighborCol >= WorldSizeX) {
continue;
}
if(visited[neighborRow][neighborCol]) {
continue;
}
// Mark the neighboring cell as visited and enqueue it
if(world.matrix[neighborRow][neighborCol] != 2) {
visited[neighborRow][neighborCol] = 1;
Cell neighborCell = {.row = neighborRow, .col = neighborCol};
enqueue(&q, neighborCell);
}
}
}
}
return false;
}
static void draw_callback(Canvas* canvas, void* ctx) {
furi_assert(ctx);
const BomberState* bomber_state = ctx;
furi_mutex_acquire(bomber_state->mutex, FuriWaitForever);
if(!BFS()) {
init();
}
canvas_clear(canvas);
canvas_draw_icon(canvas, world.endx * 10 + 4, world.endy * 10 + 2, &I_end);
if(world.running) {
for(size_t i = 0; i < WorldSizeY; i++) {
for(size_t j = 0; j < WorldSizeX; j++) {
switch(world.matrix[i][j]) {
case 0:
break;
case 1:
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_box);
break;
case 2:
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_unbreakbox);
break;
case 3:
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb0);
break;
case 4:
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb1);
break;
case 5:
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb2);
break;
case 6:
canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_explore);
world.matrix[i][j] = 0;
break;
}
}
}
if(world.player->side) {
canvas_draw_icon(
canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerright);
} else {
canvas_draw_icon(
canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerleft);
}
for(int i = 0; i < world.enemies_count; i++) {
if(world.enemies[i].level > 0) {
canvas_draw_icon(
canvas, world.enemies[i].x * 10 + 4, world.enemies[i].y * 10 + 2, &I_enemy1);
} else {
if(world.enemies[i].side) {
canvas_draw_icon(
canvas,
world.enemies[i].x * 10 + 4,
world.enemies[i].y * 10 + 2,
&I_enemyright);
} else {
canvas_draw_icon(
canvas,
world.enemies[i].x * 10 + 4,
world.enemies[i].y * 10 + 2,
&I_enemyleft);
}
}
}
} else {
canvas_set_font(canvas, FontPrimary);
if(world.player->x == world.endx && world.player->y == world.endy) {
if(world.level == 20) {
canvas_draw_str(canvas, 30, 35, "You win!");
} else {
canvas_draw_str(canvas, 30, 35, "Next level!");
char str[20];
intToStr(world.level, str);
canvas_draw_str(canvas, 90, 35, str);
}
} else {
canvas_draw_str(canvas, 30, 35, "You died :(");
}
}
furi_mutex_release(bomber_state->mutex);
}
static void input_callback(InputEvent* input_event, void* ctx) {
// Проверяем, что контекст не нулевой
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
}
int32_t bomberduck_app(void* p) {
UNUSED(p);
// Текущее событие типа InputEvent
InputEvent event;
// Очередь событий на 8 элементов размера InputEvent
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
BomberState* bomber_state = malloc(sizeof(BomberState));
bomber_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); // Alloc Mutex
if(!bomber_state->mutex) {
FURI_LOG_E("BomberDuck", "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(bomber_state);
return 255;
}
// Создаем новый view port
ViewPort* view_port = view_port_alloc();
// Создаем callback отрисовки, без контекста
view_port_draw_callback_set(view_port, draw_callback, bomber_state);
// Создаем callback нажатий на клавиши, в качестве контекста передаем
// нашу очередь сообщений, чтоб запихивать в неё эти события
view_port_input_callback_set(view_port, input_callback, event_queue);
// Создаем GUI приложения
Gui* gui = furi_record_open(RECORD_GUI);
// Подключаем view port к GUI в полноэкранном режиме
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message_block(notification, &sequence_display_backlight_enforce_on);
init();
// Бесконечный цикл обработки очереди событий
while(1) {
if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
furi_mutex_acquire(bomber_state->mutex, FuriWaitForever);
// Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения
if(event.type == InputTypePress) {
if(event.key == InputKeyOk) {
if(world.running) {
if(world.matrix[world.player->y][world.player->x] == 0 &&
world.bombs_count < 2) {
notification_message(notification, &bomb2);
world.matrix[world.player->y][world.player->x] = 3;
Bomb bomb = {world.player->x, world.player->y, furi_get_tick()};
world.bombs[world.bombs_count] = bomb;
world.bombs_count++;
}
} else {
init();
}
}
if(world.running) {
if(event.key == InputKeyUp) {
if(world.player->y > 0 &&
world.matrix[world.player->y - 1][world.player->x] == 0)
world.player->y--;
}
if(event.key == InputKeyDown) {
if(world.player->y < WorldSizeY - 1 &&
world.matrix[world.player->y + 1][world.player->x] == 0)
world.player->y++;
}
if(event.key == InputKeyLeft) {
world.player->side = 0;
if(world.player->x > 0 &&
world.matrix[world.player->y][world.player->x - 1] == 0)
world.player->x--;
}
if(event.key == InputKeyRight) {
world.player->side = 1;
if(world.player->x < WorldSizeX - 1 &&
world.matrix[world.player->y][world.player->x + 1] == 0)
world.player->x++;
}
}
} else if(event.type == InputTypeLong) {
if(event.key == InputKeyBack) {
break;
}
}
}
if(world.running) {
if(world.player->x == world.endx && world.player->y == world.endy) {
notification_message(notification, &end);
world.running = 0;
world.level += 1;
}
for(int i = 0; i < world.bombs_count; i++) {
if(furi_get_tick() - world.bombs[i].planted >
(unsigned long)max((3000 - world.level * 150), 1000)) {
vibration = false;
world.matrix[world.bombs[i].y][world.bombs[i].x] = 6;
notification_message(notification, &bomb_explore);
for(int j = max(0, world.bombs[i].y - BombRange);
j < min(WorldSizeY, world.bombs[i].y + BombRange + 1);
j++) {
if(world.matrix[j][world.bombs[i].x] != 2) {
world.matrix[j][world.bombs[i].x] = 6;
if(j == world.player->y && world.bombs[i].x == world.player->x) {
notification_message(notification, &end);
world.running = 0;
}
for(int e = 0; e < world.enemies_count; e++) {
if(j == world.enemies[e].y &&
world.bombs[i].x == world.enemies[e].x) {
if(world.enemies[e].level > 0) {
world.enemies[e].level--;
} else {
for(int l = e; l < world.enemies_count - 1; l++) {
world.enemies[l] = world.enemies[l + 1];
}
world.enemies_count--;
}
}
}
}
}
for(int j = max(0, world.bombs[i].x - BombRange);
j < min(WorldSizeX, world.bombs[i].x + BombRange + 1);
j++) {
if(world.matrix[world.bombs[i].y][j] != 2) {
world.matrix[world.bombs[i].y][j] = 6;
if(world.bombs[i].y == world.player->y && j == world.player->x) {
notification_message(notification, &end);
world.running = 0;
}
for(int e = 0; e < world.enemies_count; e++) {
if(world.bombs[i].y == world.enemies[e].y &&
j == world.enemies[e].x) {
if(world.enemies[e].level > 0) {
world.enemies[e].level--;
} else {
for(int l = e; l < world.enemies_count - 1; l++) {
world.enemies[l] = world.enemies[l + 1];
}
world.enemies_count--;
}
}
}
}
}
for(int j = i; j < world.bombs_count - 1; j++) {
world.bombs[j] = world.bombs[j + 1];
}
world.bombs_count--;
} else if(
furi_get_tick() - world.bombs[i].planted >
(unsigned long)max((3000 - world.level * 150) * 2 / 3, 666) &&
world.matrix[world.bombs[i].y][world.bombs[i].x] != 5) {
world.matrix[world.bombs[i].y][world.bombs[i].x] = 5;
vibration = true;
} else if(
furi_get_tick() - world.bombs[i].planted >
(unsigned long)max((3000 - world.level * 150) / 3, 333) &&
world.matrix[world.bombs[i].y][world.bombs[i].x] != 4) {
world.matrix[world.bombs[i].y][world.bombs[i].x] = 4;
}
}
for(int e = 0; e < world.enemies_count; e++) {
if(world.player->y == world.enemies[e].y &&
world.player->x == world.enemies[e].x) {
notification_message(notification, &end);
world.running = 0;
}
}
for(int e = 0; e < world.enemies_count; e++) {
if(world.enemies[e].level > 0) {
if(furi_get_tick() - world.enemies[e].last >
(unsigned long)max((2000 - world.level * 100), 1000)) {
world.enemies[e].last = furi_get_tick();
int move = rand() % 4;
switch(move) {
case 0:
if(world.enemies[e].y > 0 &&
world.matrix[world.enemies[e].y - 1][world.enemies[e].x] != 2)
world.enemies[e].y--;
break;
case 1:
if(world.enemies[e].y < WorldSizeY - 1 &&
world.matrix[world.enemies[e].y + 1][world.enemies[e].x] != 2)
world.enemies[e].y++;
break;
case 2:
world.enemies[e].side = 0;
if(world.enemies[e].x > 0 &&
world.matrix[world.enemies[e].y][world.enemies[e].x - 1] != 2)
world.enemies[e].x--;
break;
case 3:
world.enemies[e].side = 1;
if(world.enemies[e].x < WorldSizeX - 1 &&
world.matrix[world.enemies[e].y][world.enemies[e].x + 1] != 2)
world.enemies[e].x++;
default:
break;
}
}
} else {
if(furi_get_tick() - world.enemies[e].last >
(unsigned long)max((1000 - world.level * 50), 500)) {
world.enemies[e].last = furi_get_tick();
int move = rand() % 4;
switch(move) {
case 0:
if(world.enemies[e].y > 0 &&
world.matrix[world.enemies[e].y - 1][world.enemies[e].x] == 0)
world.enemies[e].y--;
break;
case 1:
if(world.enemies[e].y < WorldSizeY - 1 &&
world.matrix[world.enemies[e].y + 1][world.enemies[e].x] == 0)
world.enemies[e].y++;
break;
case 2:
world.enemies[e].side = 0;
if(world.enemies[e].x > 0 &&
world.matrix[world.enemies[e].y][world.enemies[e].x - 1] == 0)
world.enemies[e].x--;
break;
case 3:
world.enemies[e].side = 1;
if(world.enemies[e].x < WorldSizeX - 1 &&
world.matrix[world.enemies[e].y][world.enemies[e].x + 1] == 0)
world.enemies[e].x++;
default:
break;
}
}
}
}
for(int e = 0; e < world.enemies_count; e++) {
for(int h = e + 1; h < world.enemies_count; h++) {
if(world.enemies[e].y == world.enemies[h].y &&
world.enemies[e].x == world.enemies[h].x) {
world.enemies[h].level++;
for(int l = e; l < world.enemies_count - 1; l++) {
world.enemies[l] = world.enemies[l + 1];
}
world.enemies_count--;
}
}
}
if(vibration) {
notification_message(notification, &vibr1);
}
}
view_port_update(view_port);
furi_mutex_release(bomber_state->mutex);
}
// Return to normal backlight settings
notification_message_block(notification, &sequence_display_backlight_enforce_auto);
furi_record_close(RECORD_NOTIFICATION);
// Специальная очистка памяти, занимаемой очередью
furi_message_queue_free(event_queue);
// Чистим созданные объекты, связанные с интерфейсом
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_mutex_free(bomber_state->mutex);
furi_record_close(RECORD_GUI);
free(bomber_state);
return 0;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 B

+1 -1
View File
@@ -111,7 +111,7 @@ void rShift() {
memset((tmp + stackSize) - BF_STACK_STEP_SIZE, 0x00, BF_STACK_STEP_SIZE);
bfStack = (uint8_t*)tmp;
};
}
if(stackPtr > stackSizeReal) {
stackSizeReal = stackPtr;
}
+1 -1
View File
@@ -199,7 +199,7 @@ void generate_calculator_layout(Canvas* canvas) {
canvas_draw_str(canvas, 19, 118, " 0");
canvas_draw_str(canvas, 35, 118, " .");
canvas_draw_str(canvas, 51, 118, " =");
};
}
void calculator_draw_callback(Canvas* canvas, void* ctx) {
furi_assert(ctx);
+1 -6
View File
@@ -72,12 +72,7 @@ void latch_tx_handler() {
session.is_connected = &session_connected;
cli_session_close(global_cli);
cli_session_open(global_cli, &session);
// Unlock loader-lock
Loader* loader = furi_record_open(RECORD_LOADER);
Loader_internal* loader_i = (Loader_internal*)loader;
loader_i->lock_count = 0;
furi_record_close(RECORD_CLI);
furi_record_close(RECORD_LOADER);
}
void unlatch_tx_handler(bool persist) {
Cli* global_cli = furi_record_open(RECORD_CLI);
@@ -106,4 +101,4 @@ void unlatch_tx_handler(bool persist) {
// At this point, all cli_vcp functions should be back.
furi_stream_buffer_free(rx_stream);
furi_stream_buffer_free(tx_stream);
}
}
+14 -1
View File
@@ -65,7 +65,13 @@ static void input_callback_wrapper(InputEvent* event, void* context) {
int32_t cligui_main(void* p) {
UNUSED(p);
loader_unlock(furi_record_open(RECORD_LOADER));
// Unlock loader-lock and save app thread
FuriThread* temp_save_appthr;
Loader* loader = furi_record_open(RECORD_LOADER);
Loader_internal* loader_i = (Loader_internal*)loader;
temp_save_appthr = loader_i->app.thread;
loader_unlock(loader);
furi_record_close(RECORD_LOADER);
CliguiApp* cligui = malloc(sizeof(CliguiApp));
@@ -132,5 +138,12 @@ int32_t cligui_main(void* p) {
free(cligui->data);
free(cligui);
// Don't touch system loader!!! We restoring previous app thread here, we love kostily and velosipedy, bydlo kod forever!
Loader* loader1 = furi_record_open(RECORD_LOADER);
Loader_internal* loader_ii = (Loader_internal*)loader1;
loader_ii->app.thread = temp_save_appthr;
furi_record_close(RECORD_LOADER);
return 0;
}
+25 -13
View File
@@ -5,6 +5,10 @@
#include <m-bptree.h>
#include <m-array.h>
#include <cli/cli.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/menu.h>
#include <gui/modules/submenu.h>
typedef struct {
FuriThreadStdoutWriteCallback write_callback;
@@ -68,24 +72,32 @@ typedef struct {
} ViewPort_internal;
typedef struct {
FuriThreadId loader_thread;
Gui* gui;
ViewDispatcher* view_dispatcher;
Menu* primary_menu;
Submenu* settings_menu;
const void* application;
FuriThread* application_thread;
char* application_arguments;
void (*closed_callback)(void*);
void* closed_callback_context;
void* cli;
void* gui;
void (*click_callback)(const char*, void*);
void* click_callback_context;
void* view_dispatcher;
void* primary_menu;
void* plugins_menu;
void* debug_menu;
void* settings_menu;
FuriThread* thread;
} LoaderMenu_internal;
volatile uint8_t lock_count;
typedef struct {
char* args;
char* name;
FuriThread* thread;
bool insomniac;
} LoaderAppData_internal;
void* pubsub;
typedef struct {
FuriPubSub* pubsub;
FuriMessageQueue* queue;
LoaderMenu_internal* loader_menu;
LoaderAppData_internal app;
} Loader_internal;
typedef struct {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

+1 -1
View File
@@ -393,7 +393,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const
if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
FURI_LOG_E(TAG, "Unable to open file");
break;
};
}
uint16_t ret = 0;
do {
-3
View File
@@ -121,9 +121,6 @@ void flipfrid_free(FlipFridState* flipfrid) {
furi_string_free(flipfrid->main_menu_proto_items[i]);
}
free(flipfrid->data);
free(flipfrid->payload);
// The rest
free(flipfrid);
}
@@ -356,7 +356,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
stream_rewind(context->uids_stream);
end_of_list = true;
break;
};
}
if(furi_string_get_char(context->data_str, 0) == '#') continue;
if(furi_string_size(context->data_str) != 11) break;
break;
@@ -370,7 +370,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_error);
break;
};
}
// string is valid, parse it in context->payload
for(uint8_t i = 0; i < 5; i++) {
@@ -394,7 +394,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
stream_rewind(context->uids_stream);
end_of_list = true;
break;
};
}
if(furi_string_get_char(context->data_str, 0) == '#') continue;
if(furi_string_size(context->data_str) != 9) break;
break;
@@ -408,7 +408,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_error);
break;
};
}
// string is valid, parse it in context->payload
for(uint8_t i = 0; i < 4; i++) {
+39 -20
View File
@@ -39,33 +39,53 @@ typedef struct {
static void draw_callback(Canvas* canvas, void* ctx) {
furi_assert(ctx);
mutexStruct displayStruct;
mutexStruct* geigerMutex = ctx;
furi_mutex_acquire(geigerMutex->mutex, FuriWaitForever);
memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct));
furi_mutex_release(geigerMutex->mutex);
mutexStruct* mutexVal = ctx;
mutexStruct mutexDraw;
furi_mutex_acquire(mutexVal->mutex, FuriWaitForever);
memcpy(&mutexDraw, mutexVal, sizeof(mutexStruct));
furi_mutex_release(mutexVal->mutex);
char buffer[32];
if(displayStruct.data == 0)
snprintf(
buffer, sizeof(buffer), "%ld cps - %ld cpm", displayStruct.cps, displayStruct.cpm);
else if(displayStruct.data == 1)
if(mutexDraw.data == 0)
snprintf(buffer, sizeof(buffer), "%ld cps - %ld cpm", mutexDraw.cps, mutexDraw.cpm);
else if(mutexDraw.data == 1)
snprintf(
buffer,
sizeof(buffer),
"%ld cps - %.2f uSv/h",
displayStruct.cps,
((double)displayStruct.cpm * (double)CONVERSION_FACTOR));
else
mutexDraw.cps,
((double)mutexDraw.cpm * (double)CONVERSION_FACTOR));
else if(mutexDraw.data == 2)
snprintf(
buffer,
sizeof(buffer),
"%ld cps - %.2f mSv/y",
displayStruct.cps,
(((double)displayStruct.cpm * (double)CONVERSION_FACTOR)) * (double)8.76);
mutexDraw.cps,
(((double)mutexDraw.cpm * (double)CONVERSION_FACTOR)) * (double)8.76);
else if(mutexDraw.data == 3)
snprintf(
buffer,
sizeof(buffer),
"%ld cps - %.4f Rad/h",
mutexDraw.cps,
((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) / (double)10000);
else if(mutexDraw.data == 4)
snprintf(
buffer,
sizeof(buffer),
"%ld cps - %.2f mR/h",
mutexDraw.cps,
((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) / (double)10);
else
snprintf(
buffer,
sizeof(buffer),
"%ld cps - %.2f uR/h",
mutexDraw.cps,
((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) * (double)100);
for(int i = 0; i < SCREEN_SIZE_X; i += 2) {
float Y = SCREEN_SIZE_Y - (displayStruct.line[i / 2] * displayStruct.coef);
float Y = SCREEN_SIZE_Y - (mutexDraw.line[i / 2] * mutexDraw.coef);
canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y);
canvas_draw_line(canvas, i + 1, Y, i + 1, SCREEN_SIZE_Y);
@@ -103,8 +123,7 @@ static void gpiocallback(void* ctx) {
furi_message_queue_put(queue, &event, 0);
}
int32_t flipper_geiger_app(void* p) {
UNUSED(p);
int32_t flipper_geiger_app() {
EventApp event;
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp));
@@ -127,7 +146,7 @@ int32_t flipper_geiger_app(void* p) {
}
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, draw_callback, &mutexVal);
view_port_draw_callback_set(view_port, draw_callback, &mutexVal.mutex);
view_port_input_callback_set(view_port, input_callback, event_queue);
furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue);
@@ -167,7 +186,7 @@ int32_t flipper_geiger_app(void* p) {
if(mutexVal.data != 0)
mutexVal.data--;
else
mutexVal.data = 2;
mutexVal.data = 5;
screenRefresh = 1;
furi_mutex_release(mutexVal.mutex);
@@ -175,7 +194,7 @@ int32_t flipper_geiger_app(void* p) {
event.input.type == InputTypeShort)) {
furi_mutex_acquire(mutexVal.mutex, FuriWaitForever);
if(mutexVal.data != 2)
if(mutexVal.data != 5)
mutexVal.data++;
else
mutexVal.data = 0;

Before

Width:  |  Height:  |  Size: 135 B

After

Width:  |  Height:  |  Size: 135 B

Before

Width:  |  Height:  |  Size: 135 B

After

Width:  |  Height:  |  Size: 135 B

@@ -82,7 +82,7 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
if(model->rx_active)
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
else
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180);
}
static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {
+1 -1
View File
@@ -163,7 +163,7 @@ static bool hex_viewer_open_file(HexViewer* hex_viewer, const char* file_path) {
FURI_LOG_E(TAG, "Unable to open stream: %s", file_path);
isOk = false;
break;
};
}
hex_viewer->model->file_size = stream_size(hex_viewer->model->stream);
} while(false);
+37
View File
@@ -9,10 +9,12 @@ enum HidDebugSubmenuIndex {
HidSubmenuIndexKeynote,
HidSubmenuIndexKeynoteVertical,
HidSubmenuIndexKeyboard,
HidSubmenuIndexNumpad,
HidSubmenuIndexMedia,
HidSubmenuIndexTikTok,
HidSubmenuIndexYTShorts,
HidSubmenuIndexMouse,
HidSubmenuIndexMouseClicker,
HidSubmenuIndexMouseJiggler,
};
@@ -28,6 +30,9 @@ static void hid_submenu_callback(void* context, uint32_t index) {
} else if(index == HidSubmenuIndexKeyboard) {
app->view_id = HidViewKeyboard;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard);
} else if(index == HidSubmenuIndexNumpad) {
app->view_id = HidViewNumpad;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewNumpad);
} else if(index == HidSubmenuIndexMedia) {
app->view_id = HidViewMedia;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia);
@@ -40,6 +45,9 @@ static void hid_submenu_callback(void* context, uint32_t index) {
} else if(index == HidSubmenuIndexYTShorts) {
app->view_id = BtHidViewYTShorts;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewYTShorts);
} else if(index == HidSubmenuIndexMouseClicker) {
app->view_id = HidViewMouseClicker;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker);
} else if(index == HidSubmenuIndexMouseJiggler) {
app->view_id = HidViewMouseJiggler;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler);
@@ -60,8 +68,10 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
hid_keynote_set_connected_status(hid->hid_keynote, connected);
hid_keynote_vertical_set_connected_status(hid->hid_keynote_vertical, connected);
hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
hid_numpad_set_connected_status(hid->hid_numpad, connected);
hid_media_set_connected_status(hid->hid_media, connected);
hid_mouse_set_connected_status(hid->hid_mouse, connected);
hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected);
hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected);
hid_tiktok_set_connected_status(hid->hid_tiktok, connected);
hid_ytshorts_set_connected_status(hid->hid_ytshorts, connected);
@@ -118,6 +128,8 @@ Hid* hid_alloc(HidTransport transport) {
app);
submenu_add_item(
app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app);
submenu_add_item(
app->device_type_submenu, "Numpad", HidSubmenuIndexNumpad, hid_submenu_callback, app);
submenu_add_item(
app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app);
submenu_add_item(
@@ -136,6 +148,12 @@ Hid* hid_alloc(HidTransport transport) {
hid_submenu_callback,
app);
}
submenu_add_item(
app->device_type_submenu,
"Mouse Clicker",
HidSubmenuIndexMouseClicker,
hid_submenu_callback,
app);
submenu_add_item(
app->device_type_submenu,
"Mouse Jiggler",
@@ -185,6 +203,12 @@ Hid* hid_app_alloc_view(void* context) {
view_dispatcher_add_view(
app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard));
//Numpad keyboard view
app->hid_numpad = hid_numpad_alloc(app);
view_set_previous_callback(hid_numpad_get_view(app->hid_numpad), hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, HidViewNumpad, hid_numpad_get_view(app->hid_numpad));
// Media view
app->hid_media = hid_media_alloc(app);
view_set_previous_callback(hid_media_get_view(app->hid_media), hid_exit_confirm_view);
@@ -209,6 +233,15 @@ Hid* hid_app_alloc_view(void* context) {
view_dispatcher_add_view(
app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse));
// Mouse clicker view
app->hid_mouse_clicker = hid_mouse_clicker_alloc(app);
view_set_previous_callback(
hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher,
HidViewMouseClicker,
hid_mouse_clicker_get_view(app->hid_mouse_clicker));
// Mouse jiggler view
app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app);
view_set_previous_callback(
@@ -240,10 +273,14 @@ void hid_free(Hid* app) {
hid_keynote_vertical_free(app->hid_keynote_vertical);
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard);
hid_keyboard_free(app->hid_keyboard);
view_dispatcher_remove_view(app->view_dispatcher, HidViewNumpad);
hid_numpad_free(app->hid_numpad);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia);
hid_media_free(app->hid_media);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse);
hid_mouse_free(app->hid_mouse);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker);
hid_mouse_clicker_free(app->hid_mouse_clicker);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler);
hid_mouse_jiggler_free(app->hid_mouse_jiggler);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);
+5 -1
View File
@@ -19,13 +19,15 @@
#include "views/hid_keynote.h"
#include "views/hid_keynote_vertical.h"
#include "views/hid_keyboard.h"
#include "views/hid_numpad.h"
#include "views/hid_media.h"
#include "views/hid_mouse.h"
#include "views/hid_mouse_jiggler.h"
#include "views/hid_tiktok.h"
#include "views/hid_ytshorts.h"
#include "views/hid_mouse_clicker.h"
#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
#include "hid_path.h"
typedef enum {
HidTransportUsb,
@@ -44,8 +46,10 @@ struct Hid {
HidKeynote* hid_keynote;
HidKeynoteVertical* hid_keynote_vertical;
HidKeyboard* hid_keyboard;
HidNumpad* hid_numpad;
HidMedia* hid_media;
HidMouse* hid_mouse;
HidMouseClicker* hid_mouse_clicker;
HidMouseJiggler* hid_mouse_jiggler;
HidTikTok* hid_tiktok;
HidYTShorts* hid_ytshorts;
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
+2
View File
@@ -3,8 +3,10 @@ typedef enum {
HidViewKeynote,
HidViewKeynoteVertical,
HidViewKeyboard,
HidViewNumpad,
HidViewMedia,
HidViewMouse,
HidViewMouseClicker,
HidViewMouseJiggler,
BtHidViewTikTok,
BtHidViewYTShorts,
+214
View File
@@ -0,0 +1,214 @@
#include "hid_mouse_clicker.h"
#include <gui/elements.h>
#include "../hid.h"
#include "hid_icons.h"
#define TAG "HidMouseClicker"
#define DEFAULT_CLICK_RATE 1
#define MAXIMUM_CLICK_RATE 60
struct HidMouseClicker {
View* view;
Hid* hid;
FuriTimer* timer;
};
typedef struct {
bool connected;
bool running;
int rate;
HidTransport transport;
} HidMouseClickerModel;
static void hid_mouse_clicker_start_or_restart_timer(void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
if(furi_timer_is_running(hid_mouse_clicker->timer)) {
furi_timer_stop(hid_mouse_clicker->timer);
}
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
furi_timer_start(
hid_mouse_clicker->timer, furi_kernel_get_tick_frequency() / model->rate);
},
true);
}
static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidMouseClickerModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Clicker");
// Ok
canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
if(model->running) {
canvas_set_font(canvas, FontPrimary);
FuriString* rate_label = furi_string_alloc();
furi_string_printf(rate_label, "%d clicks/s\n\nUp / Down", model->rate);
elements_multiline_text(canvas, AlignLeft, 35, furi_string_get_cstr(rate_label));
canvas_set_font(canvas, FontSecondary);
furi_string_free(rate_label);
elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
canvas_set_color(canvas, ColorWhite);
} else {
canvas_set_font(canvas, FontPrimary);
elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto start\nclicking");
canvas_set_font(canvas, FontSecondary);
}
canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
if(model->running) {
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop");
} else {
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start");
}
canvas_set_color(canvas, ColorBlack);
// Back
canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit");
}
static void hid_mouse_clicker_timer_callback(void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
if(model->running) {
hid_hal_mouse_press(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT);
hid_hal_mouse_release(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT);
}
},
false);
}
static void hid_mouse_clicker_enter_callback(void* context) {
hid_mouse_clicker_start_or_restart_timer(context);
}
static void hid_mouse_clicker_exit_callback(void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
furi_timer_stop(hid_mouse_clicker->timer);
}
static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) {
furi_assert(context);
HidMouseClicker* hid_mouse_clicker = context;
bool consumed = false;
bool rate_changed = false;
if(event->type != InputTypeShort && event->type != InputTypeRepeat) {
return false;
}
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
switch(event->key) {
case InputKeyOk:
model->running = !model->running;
consumed = true;
break;
case InputKeyUp:
if(model->rate < MAXIMUM_CLICK_RATE) {
model->rate++;
}
rate_changed = true;
consumed = true;
break;
case InputKeyDown:
if(model->rate > 1) {
model->rate--;
}
rate_changed = true;
consumed = true;
break;
default:
consumed = true;
break;
}
},
true);
if(rate_changed) {
hid_mouse_clicker_start_or_restart_timer(context);
}
return consumed;
}
HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) {
HidMouseClicker* hid_mouse_clicker = malloc(sizeof(HidMouseClicker));
hid_mouse_clicker->view = view_alloc();
view_set_context(hid_mouse_clicker->view, hid_mouse_clicker);
view_allocate_model(
hid_mouse_clicker->view, ViewModelTypeLocking, sizeof(HidMouseClickerModel));
view_set_draw_callback(hid_mouse_clicker->view, hid_mouse_clicker_draw_callback);
view_set_input_callback(hid_mouse_clicker->view, hid_mouse_clicker_input_callback);
view_set_enter_callback(hid_mouse_clicker->view, hid_mouse_clicker_enter_callback);
view_set_exit_callback(hid_mouse_clicker->view, hid_mouse_clicker_exit_callback);
hid_mouse_clicker->hid = hid;
hid_mouse_clicker->timer = furi_timer_alloc(
hid_mouse_clicker_timer_callback, FuriTimerTypePeriodic, hid_mouse_clicker);
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{
model->transport = hid->transport;
model->rate = DEFAULT_CLICK_RATE;
},
true);
return hid_mouse_clicker;
}
void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker) {
furi_assert(hid_mouse_clicker);
furi_timer_stop(hid_mouse_clicker->timer);
furi_timer_free(hid_mouse_clicker->timer);
view_free(hid_mouse_clicker->view);
free(hid_mouse_clicker);
}
View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker) {
furi_assert(hid_mouse_clicker);
return hid_mouse_clicker->view;
}
void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected) {
furi_assert(hid_mouse_clicker);
with_view_model(
hid_mouse_clicker->view,
HidMouseClickerModel * model,
{ model->connected = connected; },
true);
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <gui/view.h>
typedef struct Hid Hid;
typedef struct HidMouseClicker HidMouseClicker;
HidMouseClicker* hid_mouse_clicker_alloc(Hid* bt_hid);
void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker);
View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker);
void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected);
+2 -4
View File
@@ -6,8 +6,6 @@
#define TAG "HidMouseJiggler"
#define LENGTH(x) (int)(sizeof(x) / sizeof((x)[0]))
struct HidMouseJiggler {
View* view;
Hid* hid;
@@ -44,7 +42,7 @@ static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) {
elements_multiline_text(canvas, AlignLeft, 26, "Interval (ms):");
canvas_set_font(canvas, FontSecondary);
if(model->interval_idx != 0) canvas_draw_icon(canvas, 74, 19, &I_ButtonLeft_4x7);
if(model->interval_idx != LENGTH(intervals) - 1)
if(model->interval_idx != (int)COUNT_OF(intervals) - 1)
canvas_draw_icon(canvas, 80, 19, &I_ButtonRight_4x7);
FuriString* interval_str = furi_string_alloc_printf("%d", intervals[model->interval_idx]);
elements_multiline_text(canvas, 91, 26, furi_string_get_cstr(interval_str));
@@ -116,7 +114,7 @@ static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) {
consumed = true;
}
if(event->type == InputTypePress && event->key == InputKeyRight && !model->running &&
model->interval_idx < LENGTH(intervals) - 1) {
model->interval_idx < (int)COUNT_OF(intervals) - 1) {
model->interval_idx++;
consumed = true;
}
+302
View File
@@ -0,0 +1,302 @@
#include "hid_numpad.h"
#include <furi.h>
#include <gui/elements.h>
#include <gui/icon_i.h>
#include "../hid.h"
#include "hid_icons.h"
#define TAG "HidNumpad"
struct HidNumpad {
View* view;
Hid* hid;
};
typedef struct {
uint8_t last_x;
uint8_t last_y;
uint8_t x;
uint8_t y;
uint8_t last_key_code;
uint16_t modifier_code;
bool ok_pressed;
bool back_pressed;
bool connected;
char key_string[5];
HidTransport transport;
} HidNumpadModel;
typedef struct {
uint8_t width;
char* key;
uint8_t height;
const Icon* icon;
uint8_t value;
} HidNumpadKey;
typedef struct {
int8_t x;
int8_t y;
} HidNumpadPoint;
#define MARGIN_TOP 0
#define MARGIN_LEFT 24
#define KEY_WIDTH 20
#define KEY_HEIGHT 15
#define KEY_PADDING 1
#define ROW_COUNT 5
#define COLUMN_COUNT 4
const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = {
{
{.width = 1, .height = 1, .icon = NULL, .key = "NL", .value = HID_KEYPAD_NUMLOCK},
{.width = 1, .height = 1, .icon = NULL, .key = "/", .value = HID_KEYPAD_SLASH},
{.width = 1, .height = 1, .icon = NULL, .key = "*", .value = HID_KEYPAD_ASTERISK},
{.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
},
{
{.width = 1, .height = 1, .icon = NULL, .key = "7", .value = HID_KEYPAD_7},
{.width = 1, .height = 1, .icon = NULL, .key = "8", .value = HID_KEYBOARD_8},
{.width = 1, .height = 1, .icon = NULL, .key = "9", .value = HID_KEYBOARD_9},
{.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
},
{
{.width = 1, .height = 1, .icon = NULL, .key = "4", .value = HID_KEYPAD_4},
{.width = 1, .height = 1, .icon = NULL, .key = "5", .value = HID_KEYPAD_5},
{.width = 1, .height = 1, .icon = NULL, .key = "6", .value = HID_KEYPAD_6},
},
{
{.width = 1, .height = 1, .icon = NULL, .key = "1", .value = HID_KEYPAD_1},
{.width = 1, .height = 1, .icon = NULL, .key = "2", .value = HID_KEYPAD_2},
{.width = 1, .height = 1, .icon = NULL, .key = "3", .value = HID_KEYPAD_3},
{.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
},
{
{.width = 2, .height = 1, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
{.width = 0, .height = 0, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
{.width = 1, .height = 1, .icon = NULL, .key = ".", .value = HID_KEYPAD_DOT},
},
};
static void hid_numpad_draw_key(
Canvas* canvas,
HidNumpadModel* model,
uint8_t x,
uint8_t y,
HidNumpadKey key,
bool selected) {
if(!key.width || !key.height) return;
canvas_set_color(canvas, ColorBlack);
uint8_t keyWidth = KEY_WIDTH * key.width + KEY_PADDING * (key.width - 1);
uint8_t keyHeight = KEY_HEIGHT * key.height + KEY_PADDING * (key.height - 1);
if(selected) {
elements_slightly_rounded_box(
canvas,
MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING),
MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING),
keyWidth,
keyHeight);
canvas_set_color(canvas, ColorWhite);
} else {
elements_slightly_rounded_frame(
canvas,
MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING),
MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING),
keyWidth,
keyHeight);
}
if(key.icon != NULL) {
canvas_draw_icon(
canvas,
MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key.icon->width / 2,
MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + keyHeight / 2 - key.icon->height / 2,
key.icon);
} else {
strcpy(model->key_string, key.key);
canvas_draw_str_aligned(
canvas,
MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 + 1,
MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + keyHeight / 2 + 1,
AlignCenter,
AlignCenter,
model->key_string);
}
}
static void hid_numpad_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidNumpadModel* model = context;
if((!model->connected) && (model->transport == HidTransportBle)) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Numpad");
canvas_draw_icon(canvas, 68, 3, &I_Pin_back_arrow_10x8);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 127, 4, AlignRight, AlignTop, "Hold to exit");
elements_multiline_text_aligned(
canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection...");
return;
}
canvas_set_font(canvas, FontKeyboard);
uint8_t initY = model->y == 0 ? 0 : 1;
if(model->y > 5) {
initY = model->y - 4;
}
for(uint8_t y = initY; y < ROW_COUNT; y++) {
const HidNumpadKey* numpadKeyRow = hid_numpad_keyset[y];
uint8_t x = 0;
for(uint8_t i = 0; i < COLUMN_COUNT; i++) {
HidNumpadKey key = numpadKeyRow[i];
bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y;
bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE;
hid_numpad_draw_key(
canvas,
model,
x,
y - initY,
key,
(!model->ok_pressed && keySelected) || backSelected);
x += key.width;
}
}
}
static uint8_t hid_numpad_get_selected_key(HidNumpadModel* model) {
HidNumpadKey key = hid_numpad_keyset[model->y][model->x];
return key.value;
}
static void hid_numpad_get_select_key(HidNumpadModel* model, HidNumpadPoint delta) {
do {
const int delta_sum = model->y + delta.y;
model->y = delta_sum < 0 ? ROW_COUNT - 1 : delta_sum % ROW_COUNT;
} while(delta.y != 0 && hid_numpad_keyset[model->y][model->x].value == 0);
do {
const int delta_sum = model->x + delta.x;
model->x = delta_sum < 0 ? COLUMN_COUNT - 1 : delta_sum % COLUMN_COUNT;
} while(delta.x != 0 && hid_numpad_keyset[model->y][model->x].width == 0);
}
static void hid_numpad_process(HidNumpad* hid_numpad, InputEvent* event) {
with_view_model(
hid_numpad->view,
HidNumpadModel * model,
{
if(event->key == InputKeyOk) {
if(event->type == InputTypePress) {
model->ok_pressed = true;
} else if(event->type == InputTypeLong || event->type == InputTypeShort) {
model->last_key_code = hid_numpad_get_selected_key(model);
hid_hal_keyboard_press(
hid_numpad->hid, model->modifier_code | model->last_key_code);
} else if(event->type == InputTypeRelease) {
hid_hal_keyboard_release(
hid_numpad->hid, model->modifier_code | model->last_key_code);
model->ok_pressed = false;
}
} else if(event->key == InputKeyBack) {
if(event->type == InputTypePress) {
model->back_pressed = true;
} else if(event->type == InputTypeShort) {
hid_hal_keyboard_press(hid_numpad->hid, HID_KEYBOARD_DELETE);
hid_hal_keyboard_release(hid_numpad->hid, HID_KEYBOARD_DELETE);
} else if(event->type == InputTypeRelease) {
model->back_pressed = false;
}
} else if(event->type == InputTypePress || event->type == InputTypeRepeat) {
if(event->key == InputKeyUp) {
hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 0, .y = -1});
} else if(event->key == InputKeyDown) {
hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 0, .y = 1});
} else if(event->key == InputKeyLeft) {
if(model->last_x == 2 && model->last_y == 2 && model->y == 1 &&
model->x == 3) {
model->x = model->last_x;
model->y = model->last_y;
} else if(
model->last_x == 2 && model->last_y == 4 && model->y == 3 &&
model->x == 3) {
model->x = model->last_x;
model->y = model->last_y;
} else
hid_numpad_get_select_key(model, (HidNumpadPoint){.x = -1, .y = 0});
model->last_x = 0;
model->last_y = 0;
} else if(event->key == InputKeyRight) {
if(model->x == 2 && model->y == 2) {
model->last_x = model->x;
model->last_y = model->y;
hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = -1});
} else if(model->x == 2 && model->y == 4) {
model->last_x = model->x;
model->last_y = model->y;
hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = -1});
} else {
hid_numpad_get_select_key(model, (HidNumpadPoint){.x = 1, .y = 0});
}
}
}
},
true);
}
static bool hid_numpad_input_callback(InputEvent* event, void* context) {
furi_assert(context);
HidNumpad* hid_numpad = context;
bool consumed = false;
if(event->type == InputTypeLong && event->key == InputKeyBack) {
hid_hal_keyboard_release_all(hid_numpad->hid);
} else {
hid_numpad_process(hid_numpad, event);
consumed = true;
}
return consumed;
}
HidNumpad* hid_numpad_alloc(Hid* bt_hid) {
HidNumpad* hid_numpad = malloc(sizeof(HidNumpad));
hid_numpad->view = view_alloc();
hid_numpad->hid = bt_hid;
view_set_context(hid_numpad->view, hid_numpad);
view_allocate_model(hid_numpad->view, ViewModelTypeLocking, sizeof(HidNumpadModel));
view_set_draw_callback(hid_numpad->view, hid_numpad_draw_callback);
view_set_input_callback(hid_numpad->view, hid_numpad_input_callback);
with_view_model(
hid_numpad->view,
HidNumpadModel * model,
{
model->transport = bt_hid->transport;
model->y = 0;
},
true);
return hid_numpad;
}
void hid_numpad_free(HidNumpad* hid_numpad) {
furi_assert(hid_numpad);
view_free(hid_numpad->view);
free(hid_numpad);
}
View* hid_numpad_get_view(HidNumpad* hid_numpad) {
furi_assert(hid_numpad);
return hid_numpad->view;
}
void hid_numpad_set_connected_status(HidNumpad* hid_numpad, bool connected) {
furi_assert(hid_numpad);
with_view_model(
hid_numpad->view, HidNumpadModel * model, { model->connected = connected; }, true);
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <gui/view.h>
typedef struct Hid Hid;
typedef struct HidNumpad HidNumpad;
HidNumpad* hid_numpad_alloc(Hid* bt_hid);
void hid_numpad_free(HidNumpad* hid_numpad);
View* hid_numpad_get_view(HidNumpad* hid_numpad);
void hid_numpad_set_connected_status(HidNumpad* hid_numpad, bool connected);
-3
View File
@@ -121,9 +121,6 @@ void ibtnfuzzer_free(iBtnFuzzerState* ibtnfuzzer) {
furi_string_free(ibtnfuzzer->main_menu_proto_items[i]);
}
free(ibtnfuzzer->data);
free(ibtnfuzzer->payload);
// The rest
free(ibtnfuzzer);
}
@@ -242,7 +242,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) {
stream_rewind(context->uids_stream);
end_of_list = true;
break;
};
}
if(furi_string_get_char(context->data_str, 0) == '#') continue;
if(furi_string_size(context->data_str) != 17) break;
break;
@@ -256,7 +256,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) {
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_error);
break;
};
}
// string is valid, parse it in context->payload
for(uint8_t i = 0; i < 8; i++) {
@@ -280,7 +280,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) {
stream_rewind(context->uids_stream);
end_of_list = true;
break;
};
}
if(furi_string_get_char(context->data_str, 0) == '#') continue;
if(furi_string_size(context->data_str) != 5) break;
break;
@@ -294,7 +294,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) {
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_error);
break;
};
}
// string is valid, parse it in context->payload
for(uint8_t i = 0; i < 2; i++) {
@@ -318,7 +318,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) {
stream_rewind(context->uids_stream);
end_of_list = true;
break;
};
}
if(furi_string_get_char(context->data_str, 0) == '#') continue;
if(furi_string_size(context->data_str) != 9) break;
break;
@@ -332,7 +332,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) {
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_error);
break;
};
}
// string is valid, parse it in context->payload
for(uint8_t i = 0; i < 4; i++) {
+12 -22
View File
@@ -38,6 +38,11 @@ void save_settings_file(FlipperFormat* file, Settings* settings) {
Settings* load_settings() {
Settings* settings = malloc(sizeof(Settings));
settings->save_ssid = "";
settings->save_password = "";
settings->save_key = "";
settings->save_event = "";
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file = flipper_format_file_alloc(storage);
@@ -53,29 +58,14 @@ Settings* load_settings() {
text_event_value = furi_string_alloc();
if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) {
if(!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) {
flipper_format_file_close(file);
} else {
settings->save_ssid = malloc(1);
settings->save_password = malloc(1);
settings->save_key = malloc(1);
settings->save_event = malloc(1);
settings->save_ssid[0] = '\0';
settings->save_password[0] = '\0';
settings->save_key[0] = '\0';
settings->save_event[0] = '\0';
if(flipper_format_file_open_new(file, CONFIG_FILE_PATH)) {
save_settings_file(file, settings);
flipper_format_file_close(file);
}
flipper_format_file_close(file);
} else {
if(!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
flipper_format_file_close(file);
} else {
if(flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) {
uint32_t value;
if(!flipper_format_read_header(file, string_value, &value)) {
} else {
if(flipper_format_read_header(file, string_value, &value)) {
if(flipper_format_read_string(file, CONF_SSID, text_ssid_value)) {
settings->save_ssid = malloc(furi_string_size(text_ssid_value) + 1);
strcpy(settings->save_ssid, furi_string_get_cstr(text_ssid_value));
@@ -93,8 +83,8 @@ Settings* load_settings() {
strcpy(settings->save_event, furi_string_get_cstr(text_event_value));
}
}
flipper_format_file_close(file);
}
flipper_format_file_close(file);
}
furi_string_free(text_ssid_value);
@@ -139,7 +129,7 @@ void send_serial_command_config(ESerialCommand command, Settings* settings) {
break;
default:
return;
};
}
furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
}
@@ -248,4 +238,4 @@ int32_t ifttt_virtual_button_app(void* p) {
view_dispatcher_run(app->view_dispatcher);
ifttt_virtual_button_app_free(app);
return 0;
}
}
+1 -1
View File
@@ -38,7 +38,7 @@ void send_serial_command_send(ESerialCommand command) {
break;
default:
return;
};
}
furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

+1 -1
View File
@@ -178,7 +178,7 @@ static void timer_callback(void* ctx) {
case Silent:
break;
}
};
}
// this is a bit of a kludge... if we are on vibro and unpronounced, stop vibro after half the usual duration
switch(metronome_state->output_mode) {
+1
View File
@@ -2,6 +2,7 @@ App(
appid="mfkey32",
name="Mfkey32",
apptype=FlipperAppType.EXTERNAL,
targets=["f7"],
entry_point="mfkey32_main",
requires=[
"gui",
+157 -95
View File
@@ -1,8 +1,9 @@
#pragma GCC optimize("O3")
#pragma GCC optimize("-funroll-all-loops")
// TODO: Handle back button correctly
// TODO: Add keys to top of the user dictionary, not the bottom
// TODO: More efficient dictionary bruteforce by scanning through hardcoded very common keys and previously found dictionary keys first?
// (a cache for napi_key_already_found_for_nonce)
#include <furi.h>
#include <furi_hal.h>
@@ -22,6 +23,7 @@
#include <lib/toolbox/args.h>
#include <lib/flipper_format/flipper_format.h>
#include <dolphin/dolphin.h>
#include <notification/notification_messages.h>
#define MF_CLASSIC_DICT_FLIPPER_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc")
#define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc")
@@ -29,9 +31,7 @@
#define TAG "Mfkey32"
#define NFC_MF_CLASSIC_KEY_LEN (13)
// MSB_LIMIT: Chunk size (out of 256)
#define MSB_LIMIT 16
#define MIN_RAM 114500
#define MIN_RAM 115632
#define LF_POLY_ODD (0x29CE5C)
#define LF_POLY_EVEN (0x870804)
#define CONST_M1_1 (LF_POLY_EVEN << 1 | 1)
@@ -40,9 +40,15 @@
#define CONST_M2_2 (LF_POLY_EVEN << 1 | 1)
#define BIT(x, n) ((x) >> (n)&1)
#define BEBIT(x, n) BIT(x, (n) ^ 24)
#define SWAPENDIAN(x) (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16)
#define SWAPENDIAN(x) \
((x) = ((x) >> 8 & 0xff00ff) | ((x)&0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16)
//#define SIZEOF(arr) sizeof(arr) / sizeof(*arr)
static int eta_round_time = 56;
static int eta_total_time = 900;
// MSB_LIMIT: Chunk size (out of 256)
static int MSB_LIMIT = 16;
struct Crypto1State {
uint32_t odd, even;
};
@@ -68,7 +74,6 @@ typedef struct {
typedef enum {
MissingNonces,
ZeroNonces,
OutOfMemory,
} MfkeyError;
typedef enum {
@@ -81,15 +86,20 @@ typedef enum {
Help,
} MfkeyState;
// TODO: Can we eliminate any of the members of this struct?
typedef struct {
FuriMutex* mutex;
MfkeyError err;
MfkeyState mfkey_state;
int cracked;
int unique_cracked;
int num_completed;
int total;
int dict_count;
int search;
int eta_timestamp;
int eta_total;
int eta_round;
bool is_thread_running;
bool close_thread_please;
FuriThread* mfkeythread;
@@ -194,7 +204,7 @@ static inline uint32_t crypt_word(struct Crypto1State* s) {
uint32_t res_ret = 0;
uint32_t feedin, t;
for(int i = 0; i <= 31; i++) {
res_ret |= (filter(s->odd) << (24 ^ i));
res_ret |= (filter(s->odd) << (24 ^ i)); //-V629
feedin = LF_POLY_EVEN & s->even;
feedin ^= LF_POLY_ODD & s->odd;
s->even = s->even << 1 | (evenparity32(feedin));
@@ -431,6 +441,17 @@ int old_recover(
return s;
}
static inline int sync_state(ProgramState* program_state) {
int ts = furi_hal_rtc_get_timestamp();
program_state->eta_round = program_state->eta_round - (ts - program_state->eta_timestamp);
program_state->eta_total = program_state->eta_total - (ts - program_state->eta_timestamp);
program_state->eta_timestamp = ts;
if(program_state->close_thread_please) {
return 1;
}
return 0;
}
int calculate_msb_tables(
int oks,
int eks,
@@ -440,7 +461,8 @@ int calculate_msb_tables(
struct Msb* odd_msbs,
struct Msb* even_msbs,
unsigned int* temp_states_odd,
unsigned int* temp_states_even) {
unsigned int* temp_states_even,
ProgramState* program_state) {
//FURI_LOG_I(TAG, "MSB GO %i", msb_iter); // DEBUG
unsigned int msb_head = (MSB_LIMIT * msb_round); // msb_iter ranges from 0 to (256/MSB_LIMIT)-1
unsigned int msb_tail = (MSB_LIMIT * (msb_round + 1));
@@ -452,10 +474,13 @@ int calculate_msb_tables(
memset(even_msbs, 0, MSB_LIMIT * sizeof(struct Msb));
for(semi_state = 1 << 20; semi_state >= 0; semi_state--) {
//if (main_iter % 2048 == 0) {
// FURI_LOG_I(TAG, "On main_iter %i", main_iter); // DEBUG
//}
if(filter(semi_state) == (oks & 1)) {
if(semi_state % 32768 == 0) {
if(sync_state(program_state) == 1) {
return 0;
}
}
if(filter(semi_state) == (oks & 1)) { //-V547
states_buffer[0] = semi_state;
states_tail = state_loop(states_buffer, oks, CONST_M1_1, CONST_M2_1);
@@ -478,7 +503,7 @@ int calculate_msb_tables(
}
}
if(filter(semi_state) == (eks & 1)) {
if(filter(semi_state) == (eks & 1)) { //-V547
states_buffer[0] = semi_state;
states_tail = state_loop(states_buffer, eks, CONST_M1_2, CONST_M2_2);
@@ -507,6 +532,12 @@ int calculate_msb_tables(
eks >>= 12;
for(i = 0; i < MSB_LIMIT; i++) {
if(sync_state(program_state) == 1) {
return 0;
}
// TODO: Why is this necessary?
memset(temp_states_even, 0, sizeof(unsigned int) * (1280));
memset(temp_states_odd, 0, sizeof(unsigned int) * (1280));
memcpy(temp_states_odd, odd_msbs[i].states, odd_msbs[i].tail * sizeof(unsigned int));
memcpy(temp_states_even, even_msbs[i].states, even_msbs[i].tail * sizeof(unsigned int));
int res = old_recover(
@@ -532,7 +563,8 @@ int calculate_msb_tables(
return 0;
}
int recover(struct Crypto1Params* p, int ks2, ProgramState* const program_state) {
bool recover(struct Crypto1Params* p, int ks2, ProgramState* program_state) {
bool found = false;
unsigned int* states_buffer = malloc(sizeof(unsigned int) * (2 << 9));
struct Msb* odd_msbs = (struct Msb*)malloc(MSB_LIMIT * sizeof(struct Msb));
struct Msb* even_msbs = (struct Msb*)malloc(MSB_LIMIT * sizeof(struct Msb));
@@ -547,9 +579,12 @@ int recover(struct Crypto1Params* p, int ks2, ProgramState* const program_state)
eks = eks << 1 | BEBIT(ks2, i);
}
int bench_start = furi_hal_rtc_get_timestamp();
program_state->eta_total = eta_total_time;
program_state->eta_timestamp = bench_start;
for(msb = 0; msb <= ((256 / MSB_LIMIT) - 1); msb++) {
//printf("MSB: %i\n", msb);
program_state->search = msb;
program_state->eta_round = eta_round_time;
program_state->eta_total = eta_total_time - (eta_round_time * msb);
if(calculate_msb_tables(
oks,
eks,
@@ -559,18 +594,23 @@ int recover(struct Crypto1Params* p, int ks2, ProgramState* const program_state)
odd_msbs,
even_msbs,
temp_states_odd,
temp_states_even)) {
temp_states_even,
program_state)) {
int bench_stop = furi_hal_rtc_get_timestamp();
FURI_LOG_I(TAG, "Cracked in %i seconds", bench_stop - bench_start);
free(states_buffer);
free(odd_msbs);
free(even_msbs);
free(temp_states_odd);
free(temp_states_even);
return 1;
found = true;
break;
}
if(program_state->close_thread_please) {
break;
}
}
return 0;
free(states_buffer);
free(odd_msbs);
free(even_msbs);
free(temp_states_odd);
free(temp_states_even);
return found;
}
bool napi_mf_classic_dict_check_presence(MfClassicDictType dict_type) {
@@ -820,8 +860,7 @@ MfClassicNonceArray* napi_mf_classic_nonce_array_alloc(
MfClassicDict* system_dict,
bool system_dict_exists,
MfClassicDict* user_dict,
bool user_dict_exists,
ProgramState* const program_state) {
ProgramState* program_state) {
MfClassicNonceArray* nonce_array = malloc(sizeof(MfClassicNonceArray));
MfClassicNonce* remaining_nonce_array_init = malloc(sizeof(MfClassicNonce) * 1);
nonce_array->remaining_nonce_array = remaining_nonce_array_init;
@@ -853,7 +892,7 @@ MfClassicNonceArray* napi_mf_classic_nonce_array_alloc(
// Read total amount of nonces
FuriString* next_line;
next_line = furi_string_alloc();
while(true) {
while(!(program_state->close_thread_please)) {
if(!stream_read_line(nonce_array->stream, next_line)) {
FURI_LOG_T(TAG, "No nonces left");
break;
@@ -866,56 +905,59 @@ MfClassicNonceArray* napi_mf_classic_nonce_array_alloc(
if(!furi_string_start_with_str(next_line, "Sec")) continue;
const char* next_line_cstr = furi_string_get_cstr(next_line);
MfClassicNonce res = {0};
char token[20];
int i = 0;
const char* ptr = next_line_cstr;
while(sscanf(ptr, "%s", token) == 1) {
char* endptr;
for(i = 0; i <= 17; i++) {
if(i != 0) {
next_line_cstr = strchr(next_line_cstr, ' ');
if(next_line_cstr) {
next_line_cstr++;
} else {
break;
}
}
unsigned long value = strtoul(next_line_cstr, &endptr, 16);
switch(i) {
case 5:
sscanf(token, "%lx", &res.uid);
res.uid = value;
break;
case 7:
sscanf(token, "%lx", &res.nt0);
res.nt0 = value;
break;
case 9:
sscanf(token, "%lx", &res.nr0_enc);
res.nr0_enc = value;
break;
case 11:
sscanf(token, "%lx", &res.ar0_enc);
res.ar0_enc = value;
break;
case 13:
sscanf(token, "%lx", &res.nt1);
res.nt1 = value;
break;
case 15:
sscanf(token, "%lx", &res.nr1_enc);
res.nr1_enc = value;
break;
case 17:
sscanf(token, "%lx", &res.ar1_enc);
res.ar1_enc = value;
break;
default:
break; // Do nothing
}
i++;
ptr = strchr(ptr, ' ');
if(!ptr) {
break;
}
ptr++;
next_line_cstr = endptr;
}
(program_state->total)++;
uint32_t p64b = prng_successor(res.nt1, 64);
if((system_dict_exists &&
napi_key_already_found_for_nonce(
system_dict, res.uid ^ res.nt1, res.nr1_enc, p64b, res.ar1_enc)) ||
(user_dict_exists &&
napi_key_already_found_for_nonce(
user_dict, res.uid ^ res.nt1, res.nr1_enc, p64b, res.ar1_enc))) {
(napi_key_already_found_for_nonce(
user_dict, res.uid ^ res.nt1, res.nr1_enc, p64b, res.ar1_enc))) {
(program_state->cracked)++;
(program_state->num_completed)++;
continue;
}
FURI_LOG_I(TAG, "No key found for %lx %lx", res.uid, res.ar1_enc);
FURI_LOG_I(TAG, "No key found for %8lx %8lx", res.uid, res.ar1_enc);
// TODO: Refactor
nonce_array->remaining_nonce_array = realloc(
nonce_array->remaining_nonce_array = realloc( //-V701
nonce_array->remaining_nonce_array,
sizeof(MfClassicNonce) * ((nonce_array->remaining_nonces) + 1));
nonce_array->remaining_nonces++;
@@ -923,14 +965,13 @@ MfClassicNonceArray* napi_mf_classic_nonce_array_alloc(
nonce_array->total_nonces++;
}
furi_string_free(next_line);
stream_rewind(nonce_array->stream);
buffered_file_stream_close(nonce_array->stream);
array_loaded = true;
FURI_LOG_I(TAG, "Loaded %lu nonces", nonce_array->total_nonces);
} while(false);
if(!array_loaded) {
buffered_file_stream_close(nonce_array->stream);
free(nonce_array);
nonce_array = NULL;
}
@@ -948,26 +989,23 @@ void napi_mf_classic_nonce_array_free(MfClassicNonceArray* nonce_array) {
}
static void finished_beep() {
// Beep to indicate completion if the speaker is available
if(furi_hal_speaker_acquire(1000)) { // Wait up to a second for the speaker
float freq = 3000;
float volume = 1.0f; // 100% volume
furi_hal_speaker_start(freq, volume);
furi_delay_ms(75);
furi_hal_speaker_stop();
furi_hal_speaker_release();
}
// Beep to indicate completion
NotificationApp* notification = furi_record_open("notification");
notification_message(notification, &sequence_audiovisual_alert);
notification_message(notification, &sequence_display_backlight_on);
furi_record_close("notification");
}
void mfkey32(ProgramState* const program_state) {
void mfkey32(ProgramState* program_state) {
uint64_t found_key; // recovered key
size_t keyarray_size = 0;
uint64_t* keyarray = malloc(sizeof(uint64_t) * 1);
uint32_t i = 0;
uint32_t i = 0, j = 0;
// Check for nonces
if(!napi_mf_classic_nonces_check_presence()) {
program_state->err = MissingNonces;
program_state->mfkey_state = Error;
free(keyarray);
return;
}
// Read dictionaries (optional)
@@ -990,7 +1028,7 @@ void mfkey32(ProgramState* const program_state) {
// Read nonces
MfClassicNonceArray* nonce_arr;
nonce_arr = napi_mf_classic_nonce_array_alloc(
system_dict, system_dict_exists, user_dict, user_dict_exists, program_state);
system_dict, system_dict_exists, user_dict, program_state);
if(system_dict_exists) {
napi_mf_classic_dict_free(system_dict);
}
@@ -1004,15 +1042,13 @@ void mfkey32(ProgramState* const program_state) {
return;
}
if(memmgr_get_free_heap() < MIN_RAM) {
// Insufficient RAM
program_state->err = OutOfMemory;
program_state->mfkey_state = Error;
napi_mf_classic_nonce_array_free(nonce_arr);
napi_mf_classic_dict_free(user_dict);
free(keyarray);
return;
// System has less than the guaranteed amount of RAM (140 KB) - adjust some parameters to run anyway at half speed
eta_round_time *= 2;
eta_total_time *= 2;
MSB_LIMIT /= 2;
}
program_state->mfkey_state = MfkeyAttack;
// TODO: Work backwards on this array and free memory
for(i = 0; i < nonce_arr->total_nonces; i++) {
MfClassicNonce next_nonce = nonce_arr->remaining_nonce_array[i];
uint32_t p64 = prng_successor(next_nonce.nt0, 64);
@@ -1026,9 +1062,10 @@ void mfkey32(ProgramState* const program_state) {
next_nonce.ar1_enc)) {
nonce_arr->remaining_nonces--;
(program_state->cracked)++;
(program_state->num_completed)++;
continue;
}
FURI_LOG_I(TAG, "Cracking %lx %lx", next_nonce.uid, next_nonce.ar1_enc);
FURI_LOG_I(TAG, "Cracking %8lx %8lx", next_nonce.uid, next_nonce.ar1_enc);
struct Crypto1Params p = {
0,
next_nonce.nr0_enc,
@@ -1037,24 +1074,29 @@ void mfkey32(ProgramState* const program_state) {
next_nonce.nr1_enc,
p64b,
next_nonce.ar1_enc};
if(recover(&p, next_nonce.ar0_enc ^ p64, program_state) == 0) {
if(!recover(&p, next_nonce.ar0_enc ^ p64, program_state)) {
if(program_state->close_thread_please) {
break;
}
// No key found in recover()
(program_state->num_completed)++;
continue;
}
(program_state->cracked)++;
(program_state->num_completed)++;
found_key = p.key;
bool already_found = false;
for(i = 0; i < keyarray_size; i++) {
if(keyarray[i] == found_key) {
for(j = 0; j < keyarray_size; j++) {
if(keyarray[j] == found_key) {
already_found = true;
break;
}
}
if(already_found == false) {
// New key
keyarray = realloc(keyarray, sizeof(uint64_t) * (keyarray_size + 1));
keyarray = realloc(keyarray, sizeof(uint64_t) * (keyarray_size + 1)); //-V701
keyarray_size += 1;
keyarray[keyarray_size - 1] = found_key;
(program_state->cracked)++;
(program_state->unique_cracked)++;
}
}
@@ -1068,23 +1110,28 @@ void mfkey32(ProgramState* const program_state) {
napi_mf_classic_dict_add_key_str(user_dict, temp_key);
furi_string_free(temp_key);
}
napi_mf_classic_nonce_array_free(nonce_arr);
if(user_dict_exists) {
napi_mf_classic_dict_free(user_dict);
if(keyarray_size > 0) {
// TODO: Should we use DolphinDeedNfcMfcAdd?
DOLPHIN_DEED(DolphinDeedNfcMfcAdd);
}
napi_mf_classic_nonce_array_free(nonce_arr);
napi_mf_classic_dict_free(user_dict);
free(keyarray);
//FURI_LOG_I(TAG, "mfkey32 function completed normally"); // DEBUG
program_state->mfkey_state = Complete;
finished_beep();
// No need to alert the user if they asked it to stop
if(!(program_state->close_thread_please)) {
finished_beep();
}
return;
}
// Screen is 128x64 px
static void render_callback(Canvas* const canvas, void* ctx) {
furi_assert(ctx);
const ProgramState* program_state = ctx;
ProgramState* program_state = ctx;
furi_mutex_acquire(program_state->mutex, FuriWaitForever);
char draw_str[32] = {};
char draw_str[44] = {};
canvas_clear(canvas);
canvas_draw_frame(canvas, 0, 0, 128, 64);
canvas_draw_frame(canvas, 0, 15, 128, 64);
@@ -1092,27 +1139,44 @@ static void render_callback(Canvas* const canvas, void* ctx) {
canvas_draw_str_aligned(canvas, 5, 4, AlignLeft, AlignTop, "Mfkey32");
canvas_draw_icon(canvas, 114, 4, &I_mfkey);
if(program_state->is_thread_running && program_state->mfkey_state == MfkeyAttack) {
float progress = (float)program_state->cracked / (float)program_state->total;
elements_progress_bar_with_text(canvas, 5, 18, 118, progress, draw_str);
float eta_round = (float)1 - ((float)program_state->eta_round / (float)eta_round_time);
float eta_total = (float)1 - ((float)program_state->eta_total / (float)eta_total_time);
float progress = (float)program_state->num_completed / (float)program_state->total;
if(eta_round < 0) {
// Round ETA miscalculated
eta_round = 1;
program_state->eta_round = 0;
}
if(eta_total < 0) {
// Total ETA miscalculated
eta_total = 1;
program_state->eta_total = 0;
}
canvas_set_font(canvas, FontSecondary);
snprintf(
draw_str,
sizeof(draw_str),
"Keys found: %d/%d (in prog.)",
program_state->cracked,
"Cracking: %d/%d - in prog.",
program_state->num_completed,
program_state->total);
canvas_draw_str_aligned(canvas, 5, 31, AlignLeft, AlignTop, draw_str);
elements_progress_bar_with_text(canvas, 5, 18, 118, progress, draw_str);
snprintf(
draw_str, sizeof(draw_str), "Search: %d/%d", program_state->search, 256 / MSB_LIMIT);
canvas_draw_str_aligned(canvas, 26, 41, AlignLeft, AlignTop, draw_str);
draw_str,
sizeof(draw_str),
"Round: %d/%d - ETA %02d Sec",
(program_state->search) + 1, // Zero indexed
256 / MSB_LIMIT,
program_state->eta_round);
elements_progress_bar_with_text(canvas, 5, 31, 118, eta_round, draw_str);
snprintf(draw_str, sizeof(draw_str), "Total ETA %03d Sec", program_state->eta_total);
elements_progress_bar_with_text(canvas, 5, 44, 118, eta_total, draw_str);
} else if(program_state->is_thread_running && program_state->mfkey_state == DictionaryAttack) {
elements_progress_bar_with_text(canvas, 5, 18, 118, 0, draw_str);
canvas_set_font(canvas, FontSecondary);
snprintf(
draw_str, sizeof(draw_str), "Dict solves: %d (in progress)", program_state->cracked);
canvas_draw_str_aligned(canvas, 10, 31, AlignLeft, AlignTop, draw_str);
canvas_draw_str_aligned(canvas, 10, 18, AlignLeft, AlignTop, draw_str);
snprintf(draw_str, sizeof(draw_str), "Keys in dict: %d", program_state->dict_count);
canvas_draw_str_aligned(canvas, 26, 41, AlignLeft, AlignTop, draw_str);
canvas_draw_str_aligned(canvas, 26, 28, AlignLeft, AlignTop, draw_str);
} else if(program_state->mfkey_state == Complete) {
// TODO: Scrollable list view to see cracked keys if user presses down
elements_progress_bar_with_text(canvas, 5, 18, 118, 1, draw_str);
@@ -1142,9 +1206,7 @@ static void render_callback(Canvas* const canvas, void* ctx) {
if(program_state->err == MissingNonces) {
canvas_draw_str_aligned(canvas, 25, 36, AlignLeft, AlignTop, "No nonces found");
} else if(program_state->err == ZeroNonces) {
canvas_draw_str_aligned(canvas, 25, 36, AlignLeft, AlignTop, "No nonces to crack");
} else if(program_state->err == OutOfMemory) {
canvas_draw_str_aligned(canvas, 25, 36, AlignLeft, AlignTop, "Insufficient memory");
canvas_draw_str_aligned(canvas, 15, 36, AlignLeft, AlignTop, "Nonces already cracked");
} else {
// Unhandled error
}
@@ -1161,11 +1223,12 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void mfkey32_state_init(ProgramState* const program_state) {
static void mfkey32_state_init(ProgramState* program_state) {
program_state->is_thread_running = false;
program_state->mfkey_state = Ready;
program_state->cracked = 0;
program_state->unique_cracked = 0;
program_state->num_completed = 0;
program_state->total = 0;
program_state->dict_count = 0;
}
@@ -1190,7 +1253,6 @@ void start_mfkey32_thread(ProgramState* program_state) {
int32_t mfkey32_main() {
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
DOLPHIN_DEED(DolphinDeedPluginStart);
ProgramState* program_state = malloc(sizeof(ProgramState));
mfkey32_state_init(program_state);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

+1 -1
View File
@@ -399,7 +399,7 @@ bool music_beeper_worker_load_rtttl_from_file(MusicBeeperWorker* instance, const
if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
FURI_LOG_E(TAG, "Unable to open file");
break;
};
}
uint16_t ret = 0;
do {

Some files were not shown because too many files have changed in this diff Show More