Merge remote-tracking branch 'origin/dev' into release-candidate
This commit is contained in:
commit
9524a5ef74
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@ -42,10 +42,10 @@
|
||||
/applications/examples/example_thermo/ @skotopes @DrZlo13 @hedger @gsurkov
|
||||
|
||||
# Firmware targets
|
||||
/firmware/ @skotopes @DrZlo13 @hedger @nminaylov
|
||||
/targets/ @skotopes @DrZlo13 @hedger @nminaylov
|
||||
|
||||
# Assets
|
||||
/assets/resources/infrared/ @skotopes @DrZlo13 @hedger @gsurkov
|
||||
/applications/main/infrared/resources/ @skotopes @DrZlo13 @hedger @gsurkov
|
||||
|
||||
# Documentation
|
||||
/documentation/ @skotopes @DrZlo13 @hedger @drunkbatya
|
||||
|
||||
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
|
||||
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
@ -51,11 +51,11 @@ jobs:
|
||||
- name: 'Check API versions for consistency between targets'
|
||||
run: |
|
||||
set -e
|
||||
N_API_HEADER_SIGNATURES=`ls -1 firmware/targets/f*/api_symbols.csv | xargs -I {} sh -c "head -n2 {} | md5sum" | sort -u | wc -l`
|
||||
N_API_HEADER_SIGNATURES=`ls -1 targets/f*/api_symbols.csv | xargs -I {} sh -c "head -n2 {} | md5sum" | sort -u | wc -l`
|
||||
if [ $N_API_HEADER_SIGNATURES != 1 ] ; then
|
||||
echo API versions aren\'t matching for available targets. Please update!
|
||||
echo API versions are:
|
||||
head -n2 firmware/targets/f*/api_symbols.csv
|
||||
head -n2 targets/f*/api_symbols.csv
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@ -76,7 +76,7 @@ jobs:
|
||||
mkdir artifacts map_analyser_files
|
||||
cp dist/${TARGET}-*/* artifacts/ || true
|
||||
tar czpf "artifacts/flipper-z-${TARGET}-resources-${SUFFIX}.tgz" \
|
||||
-C assets resources
|
||||
-C build/latest resources
|
||||
tar czpf "artifacts/flipper-z-${TARGET}-debugapps-${SUFFIX}.tgz" \
|
||||
-C dist/${TARGET}-*/apps/Debug .
|
||||
tar czpf "artifacts/flipper-z-${TARGET}-appsymbols-${SUFFIX}.tgz" \
|
||||
|
||||
4
.github/workflows/build_compact.yml
vendored
4
.github/workflows/build_compact.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
|
||||
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
submodules: false
|
||||
@ -46,7 +46,7 @@ jobs:
|
||||
echo "hw-target-code=$TARGET" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Deploy uFBT with SDK
|
||||
uses: flipperdevices/flipperzero-ufbt-action@v0.1.0
|
||||
uses: flipperdevices/flipperzero-ufbt-action@v0.1
|
||||
with:
|
||||
task: setup
|
||||
sdk-file: ${{ steps.build-fw.outputs.sdk-file }}
|
||||
|
||||
@ -16,14 +16,14 @@ jobs:
|
||||
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
|
||||
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
ref: ${{ github.sha }}
|
||||
|
||||
- name: 'Check protobuf branch'
|
||||
run: |
|
||||
git submodule update --init
|
||||
git submodule update --init;
|
||||
SUB_PATH="assets/protobuf";
|
||||
SUB_BRANCH="dev";
|
||||
SUB_COMMITS_MIN=40;
|
||||
|
||||
2
.github/workflows/merge_report.yml
vendored
2
.github/workflows/merge_report.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
|
||||
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
2
.github/workflows/pvs_studio.yml
vendored
2
.github/workflows/pvs_studio.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
|
||||
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
8
.github/workflows/unit_tests.yml
vendored
8
.github/workflows/unit_tests.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
@ -32,7 +32,7 @@ jobs:
|
||||
if: success()
|
||||
timeout-minutes: 10
|
||||
run: |
|
||||
./fbt flash SWD_TRANSPORT_SERIAL=2A0906016415303030303032 LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1
|
||||
./fbt resources firmware_latest flash SWD_TRANSPORT_SERIAL=2A0906016415303030303032 LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1
|
||||
|
||||
- name: 'Wait for flipper and format ext'
|
||||
id: format_ext
|
||||
@ -50,8 +50,8 @@ jobs:
|
||||
run: |
|
||||
source scripts/toolchain/fbtenv.sh
|
||||
python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
|
||||
python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/resources /ext
|
||||
python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/unit_tests /ext/unit_tests
|
||||
rm -rf build/latest/resources/dolphin
|
||||
python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send build/latest/resources /ext
|
||||
python3 scripts/power.py -p ${{steps.device.outputs.flipper}} reboot
|
||||
python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
|
||||
|
||||
|
||||
4
.github/workflows/updater_test.yml
vendored
4
.github/workflows/updater_test.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
submodules: false
|
||||
@ -56,7 +56,7 @@ jobs:
|
||||
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
|
||||
|
||||
- name: 'Checkout latest release'
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
if: failure()
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
47
SConstruct
47
SConstruct
@ -67,22 +67,22 @@ if GetOption("fullenv") or any(
|
||||
# Target for self-update package
|
||||
dist_basic_arguments = [
|
||||
"--bundlever",
|
||||
'"${UPDATE_VERSION_STRING}"',
|
||||
"${UPDATE_VERSION_STRING}",
|
||||
]
|
||||
dist_radio_arguments = [
|
||||
"--radio",
|
||||
'"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"',
|
||||
"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}",
|
||||
"--radiotype",
|
||||
"${COPRO_STACK_TYPE}",
|
||||
"${COPRO_DISCLAIMER}",
|
||||
"--obdata",
|
||||
'"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"',
|
||||
"${ROOT_DIR.abspath}/${COPRO_OB_DATA}",
|
||||
"--stackversion",
|
||||
"${COPRO_CUBE_VERSION}",
|
||||
]
|
||||
dist_resource_arguments = [
|
||||
"-r",
|
||||
'"${ROOT_DIR.abspath}/assets/resources"',
|
||||
firmware_env.subst("${RESOURCES_ROOT}"),
|
||||
]
|
||||
dist_splash_arguments = (
|
||||
[
|
||||
@ -95,7 +95,7 @@ if GetOption("fullenv") or any(
|
||||
|
||||
selfupdate_dist = distenv.DistCommand(
|
||||
"updater_package",
|
||||
(distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]),
|
||||
(distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES_MANIFEST"]),
|
||||
DIST_EXTRA=[
|
||||
*dist_basic_arguments,
|
||||
*dist_radio_arguments,
|
||||
@ -128,7 +128,8 @@ if GetOption("fullenv") or any(
|
||||
|
||||
# Installation over USB & CLI
|
||||
usb_update_package = distenv.AddUsbFlashTarget(
|
||||
"#build/usbinstall.flag", (firmware_env["FW_RESOURCES"], selfupdate_dist)
|
||||
"#build/usbinstall.flag",
|
||||
(firmware_env["FW_RESOURCES_MANIFEST"], selfupdate_dist),
|
||||
)
|
||||
distenv.Alias("flash_usb_full", usb_update_package)
|
||||
|
||||
@ -166,17 +167,27 @@ Depends(
|
||||
list(app_artifact.validator for app_artifact in external_app_list),
|
||||
)
|
||||
Alias("fap_dist", fap_dist)
|
||||
# distenv.Default(fap_dist)
|
||||
|
||||
distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_dist)
|
||||
|
||||
# Copy all faps to device
|
||||
|
||||
fap_deploy = distenv.PhonyTarget(
|
||||
"fap_deploy",
|
||||
"${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send ${SOURCE} /ext/apps",
|
||||
source=Dir("#/assets/resources/apps"),
|
||||
Action(
|
||||
[
|
||||
[
|
||||
"${PYTHON3}",
|
||||
"${FBT_SCRIPT_DIR}/storage.py",
|
||||
"-p",
|
||||
"${FLIP_PORT}",
|
||||
"send",
|
||||
"${SOURCE}",
|
||||
"/ext/apps",
|
||||
]
|
||||
]
|
||||
),
|
||||
source=firmware_env.Dir(("${RESOURCES_ROOT}/apps")),
|
||||
)
|
||||
Depends(fap_deploy, firmware_env["FW_RESOURCES_MANIFEST"])
|
||||
|
||||
|
||||
# Target for bundling core2 package for qFlipper
|
||||
@ -252,7 +263,7 @@ distenv.PhonyTarget(
|
||||
distenv.PhonyTarget(
|
||||
"debug_other_blackmagic",
|
||||
"${GDBPYCOM}",
|
||||
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
||||
GDBPYOPTS=debug_other_opts,
|
||||
)
|
||||
@ -267,13 +278,13 @@ distenv.PhonyTarget(
|
||||
# Linter
|
||||
distenv.PhonyTarget(
|
||||
"lint",
|
||||
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}",
|
||||
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]],
|
||||
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
|
||||
)
|
||||
|
||||
distenv.PhonyTarget(
|
||||
"format",
|
||||
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}",
|
||||
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]],
|
||||
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
|
||||
)
|
||||
|
||||
@ -315,11 +326,13 @@ distenv.PhonyTarget(
|
||||
|
||||
# Start Flipper CLI via PySerial's miniterm
|
||||
distenv.PhonyTarget(
|
||||
"cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}"
|
||||
"cli", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]]
|
||||
)
|
||||
|
||||
# Update WiFi devboard firmware
|
||||
distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py")
|
||||
distenv.PhonyTarget(
|
||||
"devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]]
|
||||
)
|
||||
|
||||
|
||||
# Find blackmagic probe
|
||||
@ -354,5 +367,5 @@ distenv.Alias("vscode_dist", vscode_dist)
|
||||
# Configure shell with build tools
|
||||
distenv.PhonyTarget(
|
||||
"env",
|
||||
"@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)",
|
||||
"@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)",
|
||||
)
|
||||
|
||||
@ -4,7 +4,6 @@ App(
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
targets=["f7"],
|
||||
entry_point="accessor_app",
|
||||
cdefines=["APP_ACCESSOR"],
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=40,
|
||||
|
||||
@ -3,7 +3,6 @@ App(
|
||||
name="Battery Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="battery_test_app",
|
||||
cdefines=["APP_BATTERY_TEST"],
|
||||
requires=[
|
||||
"gui",
|
||||
"power",
|
||||
|
||||
@ -3,7 +3,6 @@ App(
|
||||
name="Blink Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="blink_test_app",
|
||||
cdefines=["APP_BLINK"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=10,
|
||||
|
||||
@ -3,7 +3,6 @@ App(
|
||||
name="CCID Debug",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="ccid_test_app",
|
||||
cdefines=["CCID_TEST"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
|
||||
@ -3,7 +3,6 @@ App(
|
||||
name="Crash Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="crash_test_app",
|
||||
cdefines=["APP_CRASH_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
fap_category="Debug",
|
||||
|
||||
@ -71,7 +71,7 @@ static void direct_draw_run(DirectDraw* instance) {
|
||||
size_t counter = 0;
|
||||
float fps = 0;
|
||||
|
||||
vTaskPrioritySet(furi_thread_get_current_id(), FuriThreadPriorityIdle);
|
||||
furi_thread_set_current_priority(FuriThreadPriorityIdle);
|
||||
|
||||
do {
|
||||
size_t elapsed = DWT->CYCCNT - start;
|
||||
|
||||
@ -3,7 +3,6 @@ App(
|
||||
name="Display Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="display_test_app",
|
||||
cdefines=["APP_DISPLAY_TEST"],
|
||||
requires=["gui"],
|
||||
fap_libs=["misc"],
|
||||
stack_size=1 * 1024,
|
||||
|
||||
@ -3,7 +3,6 @@ App(
|
||||
name="File Browser Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="file_browser_app",
|
||||
cdefines=["APP_FILE_BROWSER_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=150,
|
||||
|
||||
@ -3,7 +3,6 @@ App(
|
||||
name="Keypad Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="keypad_test_app",
|
||||
cdefines=["APP_KEYPAD_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=30,
|
||||
|
||||
@ -3,7 +3,6 @@ App(
|
||||
name="Locale Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="locale_test_app",
|
||||
cdefines=["APP_LOCALE"],
|
||||
requires=["gui", "locale"],
|
||||
stack_size=2 * 1024,
|
||||
order=70,
|
||||
|
||||
@ -21,22 +21,51 @@ static void rpc_debug_app_tick_event_callback(void* context) {
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void rpc_debug_app_rpc_command_callback(RpcAppSystemEvent event, void* context) {
|
||||
static void
|
||||
rpc_debug_app_format_hex(const uint8_t* data, size_t data_size, char* buf, size_t buf_size) {
|
||||
if(data == NULL || data_size == 0) {
|
||||
strncpy(buf, "<Data empty>", buf_size);
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t byte_width = 3;
|
||||
const size_t line_width = 7;
|
||||
|
||||
data_size = MIN(data_size, buf_size / (byte_width + 1));
|
||||
|
||||
for(size_t i = 0; i < data_size; ++i) {
|
||||
char* p = buf + (i * byte_width);
|
||||
char sep = !((i + 1) % line_width) ? '\n' : ' ';
|
||||
snprintf(p, byte_width + 1, "%02X%c", data[i], sep);
|
||||
}
|
||||
|
||||
buf[buf_size - 1] = '\0';
|
||||
}
|
||||
|
||||
static void rpc_debug_app_rpc_command_callback(const RpcAppSystemEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
RpcDebugApp* app = context;
|
||||
furi_assert(app->rpc);
|
||||
|
||||
if(event == RpcAppEventSessionClose) {
|
||||
if(event->type == RpcAppEventTypeSessionClose) {
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
rpc_system_app_set_callback(app->rpc, NULL, NULL);
|
||||
app->rpc = NULL;
|
||||
} else if(event == RpcAppEventAppExit) {
|
||||
} else if(event->type == RpcAppEventTypeAppExit) {
|
||||
scene_manager_stop(app->scene_manager);
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
rpc_system_app_confirm(app->rpc, RpcAppEventAppExit, true);
|
||||
rpc_system_app_confirm(app->rpc, true);
|
||||
} else if(event->type == RpcAppEventTypeDataExchange) {
|
||||
furi_assert(event->data.type == RpcAppSystemEventDataTypeBytes);
|
||||
|
||||
rpc_debug_app_format_hex(
|
||||
event->data.bytes.ptr, event->data.bytes.size, app->text_store, TEXT_STORE_SIZE);
|
||||
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange);
|
||||
} else {
|
||||
rpc_system_app_confirm(app->rpc, event, false);
|
||||
rpc_system_app_confirm(app->rpc, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,40 +1,5 @@
|
||||
#include "../rpc_debug_app.h"
|
||||
|
||||
static void rpc_debug_app_scene_start_format_hex(
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
char* buf,
|
||||
size_t buf_size) {
|
||||
furi_assert(data);
|
||||
furi_assert(buf);
|
||||
|
||||
const size_t byte_width = 3;
|
||||
const size_t line_width = 7;
|
||||
|
||||
data_size = MIN(data_size, buf_size / (byte_width + 1));
|
||||
|
||||
for(size_t i = 0; i < data_size; ++i) {
|
||||
char* p = buf + (i * byte_width);
|
||||
char sep = !((i + 1) % line_width) ? '\n' : ' ';
|
||||
snprintf(p, byte_width + 1, "%02X%c", data[i], sep);
|
||||
}
|
||||
|
||||
buf[buf_size - 1] = '\0';
|
||||
}
|
||||
|
||||
static void rpc_debug_app_scene_receive_data_exchange_callback(
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
void* context) {
|
||||
RpcDebugApp* app = context;
|
||||
if(data) {
|
||||
rpc_debug_app_scene_start_format_hex(data, data_size, app->text_store, TEXT_STORE_SIZE);
|
||||
} else {
|
||||
strncpy(app->text_store, "<Data empty>", TEXT_STORE_SIZE);
|
||||
}
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange);
|
||||
}
|
||||
|
||||
void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) {
|
||||
RpcDebugApp* app = context;
|
||||
strncpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE);
|
||||
@ -42,8 +7,6 @@ void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) {
|
||||
text_box_set_text(app->text_box, app->text_store);
|
||||
text_box_set_font(app->text_box, TextBoxFontHex);
|
||||
|
||||
rpc_system_app_set_data_exchange_callback(
|
||||
app->rpc, rpc_debug_app_scene_receive_data_exchange_callback, app);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox);
|
||||
}
|
||||
|
||||
@ -53,6 +16,7 @@ bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneMana
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == RpcDebugAppCustomEventRpcDataExchange) {
|
||||
rpc_system_app_confirm(app->rpc, true);
|
||||
notification_message(app->notifications, &sequence_blink_cyan_100);
|
||||
notification_message(app->notifications, &sequence_display_backlight_on);
|
||||
text_box_set_text(app->text_box, app->text_store);
|
||||
@ -66,5 +30,4 @@ bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneMana
|
||||
void rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) {
|
||||
RpcDebugApp* app = context;
|
||||
text_box_reset(app->text_box);
|
||||
rpc_system_app_set_data_exchange_callback(app->rpc, NULL, NULL);
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ App(
|
||||
name="Text Box Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="text_box_test_app",
|
||||
cdefines=["APP_TEXT_BOX_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=140,
|
||||
|
||||
@ -3,7 +3,6 @@ App(
|
||||
name="UART Echo",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="uart_echo_app",
|
||||
cdefines=["APP_UART_ECHO"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=70,
|
||||
|
||||
@ -5,6 +5,7 @@ App(
|
||||
cdefines=["APP_UNIT_TESTS"],
|
||||
requires=["system_settings"],
|
||||
provides=["delay_test"],
|
||||
resources="resources",
|
||||
order=100,
|
||||
)
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ void bt_test_alloc() {
|
||||
}
|
||||
|
||||
void bt_test_free() {
|
||||
furi_assert(bt_test);
|
||||
furi_check(bt_test);
|
||||
free(bt_test->nvm_ram_buff_ref);
|
||||
free(bt_test->nvm_ram_buff_dut);
|
||||
bt_keys_storage_free(bt_test->bt_keys_storage);
|
||||
@ -89,7 +89,7 @@ static void bt_test_keys_remove_test_file() {
|
||||
}
|
||||
|
||||
MU_TEST(bt_test_keys_storage_serial_profile) {
|
||||
furi_assert(bt_test);
|
||||
furi_check(bt_test);
|
||||
|
||||
bt_test_keys_remove_test_file();
|
||||
bt_test_keys_storage_profile();
|
||||
|
||||
@ -3,18 +3,29 @@
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
void test_furi_create_open() {
|
||||
// 1. Create record
|
||||
uint8_t test_data = 0;
|
||||
furi_record_create("test/holding", (void*)&test_data);
|
||||
#define TEST_RECORD_NAME "test/holding"
|
||||
|
||||
// 2. Open it
|
||||
void* record = furi_record_open("test/holding");
|
||||
void test_furi_create_open() {
|
||||
// Test that record does not exist
|
||||
mu_check(furi_record_exists(TEST_RECORD_NAME) == false);
|
||||
|
||||
// Create record
|
||||
uint8_t test_data = 0;
|
||||
furi_record_create(TEST_RECORD_NAME, (void*)&test_data);
|
||||
|
||||
// Test that record exists
|
||||
mu_check(furi_record_exists(TEST_RECORD_NAME) == true);
|
||||
|
||||
// Open it
|
||||
void* record = furi_record_open(TEST_RECORD_NAME);
|
||||
mu_assert_pointers_eq(record, &test_data);
|
||||
|
||||
// 3. Close it
|
||||
furi_record_close("test/holding");
|
||||
// Close it
|
||||
furi_record_close(TEST_RECORD_NAME);
|
||||
|
||||
// 4. Clean up
|
||||
furi_record_destroy("test/holding");
|
||||
// Clean up
|
||||
furi_record_destroy(TEST_RECORD_NAME);
|
||||
|
||||
// Test that record does not exist
|
||||
mu_check(furi_record_exists(TEST_RECORD_NAME) == false);
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ static void infrared_test_alloc() {
|
||||
}
|
||||
|
||||
static void infrared_test_free() {
|
||||
furi_assert(test);
|
||||
furi_check(test);
|
||||
infrared_free_decoder(test->decoder_handler);
|
||||
infrared_free_encoder(test->encoder_handler);
|
||||
flipper_format_free(test->ff);
|
||||
|
||||
@ -22,7 +22,7 @@ MU_TEST(manifest_iteration_test) {
|
||||
ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(storage);
|
||||
do {
|
||||
// Open manifest file
|
||||
if(!resource_manifest_reader_open(manifest_reader, EXT_PATH("unit_tests/Manifest"))) {
|
||||
if(!resource_manifest_reader_open(manifest_reader, EXT_PATH("unit_tests/Manifest_test"))) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -81,6 +81,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
||||
|
||||
void minunit_print_progress(void);
|
||||
void minunit_print_fail(const char* error);
|
||||
void minunit_printf_warning(const char* format, ...);
|
||||
|
||||
/* Definitions */
|
||||
#define MU_TEST(method_name) static void method_name(void)
|
||||
@ -150,6 +151,10 @@ void minunit_print_fail(const char* error);
|
||||
minunit_end_proc_timer - minunit_proc_timer);)
|
||||
#define MU_EXIT_CODE minunit_fail
|
||||
|
||||
/* Warnings */
|
||||
#define mu_warn(message) \
|
||||
MU__SAFE_BLOCK(minunit_printf_warning("%s:%d: %s", __FILE__, __LINE__, message);)
|
||||
|
||||
/* Assertions */
|
||||
#define mu_check(test) \
|
||||
MU__SAFE_BLOCK( \
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
#include <nfc/nfc_poller.h>
|
||||
#include <nfc/nfc_listener.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_sync_api.h>
|
||||
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync_api.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_sync_api.h>
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>
|
||||
|
||||
#include <nfc/helpers/nfc_dict.h>
|
||||
#include <nfc/nfc.h>
|
||||
@ -34,7 +34,7 @@ static void nfc_test_alloc() {
|
||||
}
|
||||
|
||||
static void nfc_test_free() {
|
||||
furi_assert(nfc_test);
|
||||
furi_check(nfc_test);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
free(nfc_test);
|
||||
@ -182,8 +182,8 @@ MU_TEST(iso14443_3a_reader) {
|
||||
|
||||
Iso14443_3aData iso14443_3a_poller_data = {};
|
||||
mu_assert(
|
||||
iso14443_3a_poller_read(poller, &iso14443_3a_poller_data) == Iso14443_3aErrorNone,
|
||||
"iso14443_3a_poller_read() failed");
|
||||
iso14443_3a_poller_sync_read(poller, &iso14443_3a_poller_data) == Iso14443_3aErrorNone,
|
||||
"iso14443_3a_poller_sync_read() failed");
|
||||
|
||||
nfc_listener_stop(iso3_listener);
|
||||
mu_assert(
|
||||
@ -203,15 +203,26 @@ static void mf_ultralight_reader_test(const char* path) {
|
||||
NfcDevice* nfc_device = nfc_device_alloc();
|
||||
mu_assert(nfc_device_load(nfc_device, path), "nfc_device_load() failed\r\n");
|
||||
|
||||
NfcListener* mfu_listener = nfc_listener_alloc(
|
||||
listener,
|
||||
NfcProtocolMfUltralight,
|
||||
nfc_device_get_data(nfc_device, NfcProtocolMfUltralight));
|
||||
MfUltralightData* data =
|
||||
(MfUltralightData*)nfc_device_get_data(nfc_device, NfcProtocolMfUltralight);
|
||||
|
||||
uint32_t features = mf_ultralight_get_feature_support_set(data->type);
|
||||
bool pwd_supported =
|
||||
mf_ultralight_support_feature(features, MfUltralightFeatureSupportPasswordAuth);
|
||||
uint8_t pwd_num = mf_ultralight_get_pwd_page_num(data->type);
|
||||
const uint8_t zero_pwd[4] = {0, 0, 0, 0};
|
||||
|
||||
if(pwd_supported && !memcmp(data->page[pwd_num].data, zero_pwd, sizeof(zero_pwd))) {
|
||||
data->pages_read -= 2;
|
||||
}
|
||||
|
||||
NfcListener* mfu_listener = nfc_listener_alloc(listener, NfcProtocolMfUltralight, data);
|
||||
|
||||
nfc_listener_start(mfu_listener, NULL, NULL);
|
||||
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data);
|
||||
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed");
|
||||
MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
|
||||
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");
|
||||
|
||||
nfc_listener_stop(mfu_listener);
|
||||
nfc_listener_free(mfu_listener);
|
||||
@ -259,8 +270,8 @@ MU_TEST(ntag_213_locked_reader) {
|
||||
nfc_listener_start(mfu_listener, NULL, NULL);
|
||||
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data);
|
||||
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed");
|
||||
MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
|
||||
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");
|
||||
|
||||
nfc_listener_stop(mfu_listener);
|
||||
nfc_listener_free(mfu_listener);
|
||||
@ -297,8 +308,8 @@ static void mf_ultralight_write() {
|
||||
MfUltralightData* mfu_data = mf_ultralight_alloc();
|
||||
|
||||
// Initial read
|
||||
MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data);
|
||||
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed");
|
||||
MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
|
||||
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");
|
||||
|
||||
mu_assert(
|
||||
mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)),
|
||||
@ -310,13 +321,13 @@ static void mf_ultralight_write() {
|
||||
FURI_LOG_D(TAG, "Writing page %d", i);
|
||||
furi_hal_random_fill_buf(page.data, sizeof(MfUltralightPage));
|
||||
mfu_data->page[i] = page;
|
||||
error = mf_ultralight_poller_write_page(poller, i, &page);
|
||||
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_write_page() failed");
|
||||
error = mf_ultralight_poller_sync_write_page(poller, i, &page);
|
||||
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_write_page() failed");
|
||||
}
|
||||
|
||||
// Verification read
|
||||
error = mf_ultralight_poller_read_card(poller, mfu_data);
|
||||
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed");
|
||||
error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
|
||||
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");
|
||||
|
||||
nfc_listener_stop(mfu_listener);
|
||||
const MfUltralightData* mfu_listener_data =
|
||||
@ -344,7 +355,7 @@ static void mf_classic_reader() {
|
||||
MfClassicBlock block = {};
|
||||
MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
|
||||
|
||||
mf_classic_poller_read_block(poller, 0, &key, MfClassicKeyTypeA, &block);
|
||||
mf_classic_poller_sync_read_block(poller, 0, &key, MfClassicKeyTypeA, &block);
|
||||
|
||||
nfc_listener_stop(mfc_listener);
|
||||
nfc_listener_free(mfc_listener);
|
||||
@ -372,8 +383,8 @@ static void mf_classic_write() {
|
||||
furi_hal_random_fill_buf(block_write.data, sizeof(MfClassicBlock));
|
||||
MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
|
||||
|
||||
mf_classic_poller_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);
|
||||
mf_classic_poller_read_block(poller, 1, &key, MfClassicKeyTypeA, &block_read);
|
||||
mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);
|
||||
mf_classic_poller_sync_read_block(poller, 1, &key, MfClassicKeyTypeA, &block_read);
|
||||
|
||||
nfc_listener_stop(mfc_listener);
|
||||
nfc_listener_free(mfc_listener);
|
||||
@ -402,16 +413,18 @@ static void mf_classic_value_block() {
|
||||
mf_classic_value_to_block(value, 1, &block_write);
|
||||
|
||||
MfClassicError error = MfClassicErrorNone;
|
||||
error = mf_classic_poller_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);
|
||||
error = mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);
|
||||
mu_assert(error == MfClassicErrorNone, "Write failed");
|
||||
|
||||
int32_t data = 200;
|
||||
int32_t new_value = 0;
|
||||
error = mf_classic_poller_change_value(poller, 1, &key, MfClassicKeyTypeA, data, &new_value);
|
||||
error =
|
||||
mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, data, &new_value);
|
||||
mu_assert(error == MfClassicErrorNone, "Value increment failed");
|
||||
mu_assert(new_value == value + data, "Value not match");
|
||||
|
||||
error = mf_classic_poller_change_value(poller, 1, &key, MfClassicKeyTypeA, -data, &new_value);
|
||||
error =
|
||||
mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, -data, &new_value);
|
||||
mu_assert(error == MfClassicErrorNone, "Value decrement failed");
|
||||
mu_assert(new_value == value, "Value not match");
|
||||
|
||||
|
||||
@ -122,7 +122,7 @@ Nfc* nfc_alloc() {
|
||||
}
|
||||
|
||||
void nfc_free(Nfc* instance) {
|
||||
furi_assert(instance);
|
||||
furi_check(instance);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
@ -165,9 +165,9 @@ NfcError nfc_iso14443a_listener_set_col_res_data(
|
||||
uint8_t uid_len,
|
||||
uint8_t* atqa,
|
||||
uint8_t sak) {
|
||||
furi_assert(instance);
|
||||
furi_assert(uid);
|
||||
furi_assert(atqa);
|
||||
furi_check(instance);
|
||||
furi_check(uid);
|
||||
furi_check(atqa);
|
||||
|
||||
nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak);
|
||||
|
||||
@ -176,7 +176,7 @@ NfcError nfc_iso14443a_listener_set_col_res_data(
|
||||
|
||||
static int32_t nfc_worker_poller(void* context) {
|
||||
Nfc* instance = context;
|
||||
furi_assert(instance->callback);
|
||||
furi_check(instance->callback);
|
||||
|
||||
instance->state = NfcStateReady;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
@ -196,7 +196,7 @@ static int32_t nfc_worker_poller(void* context) {
|
||||
}
|
||||
|
||||
static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, uint16_t rx_bits) {
|
||||
furi_assert(instance->col_res_status != Iso14443_3aColResStatusDone);
|
||||
furi_check(instance->col_res_status != Iso14443_3aColResStatusDone);
|
||||
BitBuffer* tx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
|
||||
|
||||
bool processed = false;
|
||||
@ -255,7 +255,7 @@ static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, ui
|
||||
|
||||
static int32_t nfc_worker_listener(void* context) {
|
||||
Nfc* instance = context;
|
||||
furi_assert(instance->callback);
|
||||
furi_check(instance->callback);
|
||||
|
||||
NfcMessage message = {};
|
||||
|
||||
@ -295,17 +295,17 @@ static int32_t nfc_worker_listener(void* context) {
|
||||
}
|
||||
|
||||
void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->worker_thread == NULL);
|
||||
furi_check(instance);
|
||||
furi_check(instance->worker_thread == NULL);
|
||||
|
||||
if(instance->mode == NfcModeListener) {
|
||||
furi_assert(listener_queue == NULL);
|
||||
furi_check(listener_queue == NULL);
|
||||
// Check that poller didn't start
|
||||
furi_assert(poller_queue == NULL);
|
||||
furi_check(poller_queue == NULL);
|
||||
} else {
|
||||
furi_assert(poller_queue == NULL);
|
||||
furi_check(poller_queue == NULL);
|
||||
// Check that poller is started after listener
|
||||
furi_assert(listener_queue);
|
||||
furi_check(listener_queue);
|
||||
}
|
||||
|
||||
instance->callback = callback;
|
||||
@ -334,8 +334,8 @@ void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) {
|
||||
}
|
||||
|
||||
void nfc_stop(Nfc* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->worker_thread);
|
||||
furi_check(instance);
|
||||
furi_check(instance->worker_thread);
|
||||
|
||||
if(instance->mode == NfcModeListener) {
|
||||
NfcMessage message = {.type = NfcMessageTypeAbort};
|
||||
@ -361,10 +361,10 @@ void nfc_stop(Nfc* instance) {
|
||||
// Called from worker thread
|
||||
|
||||
NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) {
|
||||
furi_assert(instance);
|
||||
furi_assert(poller_queue);
|
||||
furi_assert(listener_queue);
|
||||
furi_assert(tx_buffer);
|
||||
furi_check(instance);
|
||||
furi_check(poller_queue);
|
||||
furi_check(listener_queue);
|
||||
furi_check(tx_buffer);
|
||||
|
||||
NfcMessage message = {};
|
||||
message.type = NfcMessageTypeTx;
|
||||
@ -382,11 +382,11 @@ NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer*
|
||||
|
||||
NfcError
|
||||
nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) {
|
||||
furi_assert(instance);
|
||||
furi_assert(tx_buffer);
|
||||
furi_assert(rx_buffer);
|
||||
furi_assert(poller_queue);
|
||||
furi_assert(listener_queue);
|
||||
furi_check(instance);
|
||||
furi_check(tx_buffer);
|
||||
furi_check(rx_buffer);
|
||||
furi_check(poller_queue);
|
||||
furi_check(listener_queue);
|
||||
UNUSED(fwt);
|
||||
|
||||
NfcError error = NfcErrorNone;
|
||||
@ -396,7 +396,7 @@ NfcError
|
||||
message.data.data_bits = bit_buffer_get_size(tx_buffer);
|
||||
bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer));
|
||||
// Tx
|
||||
furi_assert(furi_message_queue_put(listener_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
furi_check(furi_message_queue_put(listener_queue, &message, FuriWaitForever) == FuriStatusOk);
|
||||
// Rx
|
||||
FuriStatus status = furi_message_queue_get(poller_queue, &message, 50);
|
||||
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
Filetype: Flipper SubGhz Key File
|
||||
Version: 1
|
||||
Frequency: 433920000
|
||||
Preset: FuriHalSubGhzPresetOok270Async
|
||||
Protocol: Mastercode
|
||||
Bit: 36
|
||||
Key: 00 00 00 0B 7E 00 3C 08
|
||||
@ -0,0 +1,6 @@
|
||||
Filetype: Flipper SubGhz RAW File
|
||||
Version: 1
|
||||
Frequency: 433920000
|
||||
Preset: FuriHalSubGhzPresetOok270Async
|
||||
Protocol: RAW
|
||||
RAW_Data: 10389 -66 405095 -102 207 -106 1165 -130 963739 -1232 899 -2250 2003 -1190 2017 -1202 911 -2256 2021 -1162 2045 -1134 2047 -1164 2047 -1138 2031 -1180 2039 -1182 949 -2190 995 -2214 961 -2228 963 -2198 963 -2214 977 -2212 975 -2210 975 -2208 971 -2200 963 -2210 993 -2184 2075 -1130 2051 -1142 2055 -1136 2047 -1178 965 -2236 933 -2220 975 -2184 999 -2222 967 -2208 969 -2214 979 -2202 2027 -1156 975 -2242 943 -16080 2023 -1162 967 -2220 2057 -1114 2061 -1124 1007 -2242 2025 -1134 2055 -1168 2017 -1138 2075 -1134 2053 -1136 2075 -1130 979 -2214 979 -2174 999 -2182 1001 -2204 977 -2206 1003 -2188 979 -2176 999 -2182 1009 -2176 1009 -2176 1001 -2212 2029 -1116 2091 -1102 2109 -1092 2095 -1126 1001 -2150 1011 -2180 1011 -2180 1009 -2178 1009 -2172 1009 -2166 1001 -2198 2065 -1136 975 -2220 971 -16018 2097 -1166 951 -2240 2009 -1186 2011 -1160 979 -2208 2035 -1134 2053 -1138 2061 -1158 2045 -1152 2029 -1152 2051 -1166 963 -2188 993 -2222 951 -2214 963 -2220 965 -2212 979 -2212 977 -2180 1003 -2202 965 -2218 975 -2216 967 -2188 2061 -1124 2083 -1126 2071 -1130 2059 -1134 993 -2188 979 -2240 947 -2204 979 -2214 971 -2214 973 -2210 971 -2206 2053 -1130 979 -2216 969 -16056 2053 -1134 1001 -2224 2021 -1150 2051 -1154 953 -2240 2045 -1146 2023 -1168 2033 -1144 2065 -1146 2055 -1130 2071 -1160 961 -2192 973 -2190 1005 -2214 975 -2206 967 -2206 975 -2206 967 -2208 975 -2212 967 -2212 979 -2218 977 -2178 2063 -1156 2035 -1160 2061 -1126 2065 -1130 981 -2186 1003 -2210 977 -2208 973 -2202 977 -2200 965 -2248 943 -2206 2039 -1190 941 -48536 65 -7254 263 -68 363 -102 131 -232 263 -264 751 -230 225 -822 397 -634 231 -268 263 -134 267 -64 867 -132 305 -138 67 -100 331 -98 891 -66 455 -66 531 -100 299 -134 897 -98 693 -132 291 -132 333 -98 337 -68 331
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user