Merge branch 'dev' of https://github.com/ClaraCrazy/Flipper-Xtreme into detect_reader_uid_fix
@@ -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"],
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
*.swp
|
||||
*.swo
|
||||
*.gdb_history
|
||||
*.old
|
||||
|
||||
|
||||
# LSP
|
||||
|
||||
@@ -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 @@
|
||||
--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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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 $)",
|
||||
)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -34,4 +34,4 @@ private:
|
||||
|
||||
} // namespace cardboard
|
||||
|
||||
#endif // CARDBOARD_SDK_UTIL_MATRIX4X4_H_
|
||||
#endif // CARDBOARD_SDK_UTIL_MATRIX_4X4_H_
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
@@ -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;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 158 B |
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -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 {
|
||||
|
||||
@@ -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,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) {
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"
|
||||
@@ -3,8 +3,10 @@ typedef enum {
|
||||
HidViewKeynote,
|
||||
HidViewKeynoteVertical,
|
||||
HidViewKeyboard,
|
||||
HidViewNumpad,
|
||||
HidViewMedia,
|
||||
HidViewMouse,
|
||||
HidViewMouseClicker,
|
||||
HidViewMouseJiggler,
|
||||
BtHidViewTikTok,
|
||||
BtHidViewYTShorts,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ void send_serial_command_send(ESerialCommand command) {
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -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) {
|
||||
|
||||
@@ -2,6 +2,7 @@ App(
|
||||
appid="mfkey32",
|
||||
name="Mfkey32",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
targets=["f7"],
|
||||
entry_point="mfkey32_main",
|
||||
requires=[
|
||||
"gui",
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -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 {
|
||||
|
||||