From dbce54dec63ee3970f8afcb09d357ad948712990 Mon Sep 17 00:00:00 2001 From: Neil Horne <15316949+elecpower@users.noreply.github.com> Date: Sun, 14 Sep 2025 09:59:34 +1000 Subject: [PATCH] chore(cpn): refactor firmware flashing to use edgetx rs_dfu (#6521) Co-authored-by: raphaelcoeffic <1050031+raphaelcoeffic@users.noreply.github.com> --- .github/workflows/macosx_cpn.yml | 6 - .github/workflows/win_cpn-64.yml | 40 - cmake/FetchMaxLibQt.cmake | 2 +- cmake/FetchMiniz.cmake | 2 +- cmake/FetchRsDfu.cmake | 40 + cmake/FindDfuutil.cmake | 25 - cmake/FindLibusb1.cmake | 24 - cmake/NativeTargets.cmake | 18 +- companion/src/CMakeLists.txt | 76 +- companion/src/apppreferencesdialog.cpp | 103 +- companion/src/apppreferencesdialog.h | 6 +- companion/src/apppreferencesdialog.ui | 1644 +++++++++-------- companion/src/burnconfigdialog.cpp | 200 -- companion/src/burnconfigdialog.h | 74 - companion/src/burnconfigdialog.ui | 271 --- companion/src/companion.qrc | 34 - companion/src/constants.h | 10 +- companion/src/flash/CMakeLists.txt | 42 + .../src/{ => flash}/customizesplashdialog.cpp | 3 +- .../src/{ => flash}/customizesplashdialog.h | 0 .../src/{ => flash}/customizesplashdialog.ui | 0 companion/src/flash/flashfirmwaredialog.cpp | 403 ++++ .../src/{ => flash}/flashfirmwaredialog.h | 55 +- .../src/{ => flash}/flashfirmwaredialog.ui | 102 +- companion/src/{ => flash}/process_copy.cpp | 0 companion/src/{ => flash}/process_copy.h | 0 companion/src/{ => flash}/process_sync.cpp | 0 companion/src/{ => flash}/process_sync.h | 0 companion/src/flash/radiointerface.cpp | 832 +++++++++ companion/src/flash/radiointerface.h | 125 ++ companion/src/{ => flash}/splashlabel.h | 42 +- .../src/{ => flash}/splashlibrarydialog.cpp | 58 +- .../src/{ => flash}/splashlibrarydialog.h | 33 +- .../src/{ => flash}/splashlibrarydialog.ui | 130 +- companion/src/flashfirmwaredialog.cpp | 339 ---- companion/src/fusesdialog.cpp | 57 - companion/src/fusesdialog.h | 49 - companion/src/fusesdialog.ui | 118 -- companion/src/helpers.cpp | 37 +- companion/src/helpers.h | 6 +- companion/src/images/library/00001.png | Bin 2331 -> 0 bytes companion/src/images/library/00002.png | Bin 4365 -> 0 bytes companion/src/images/library/00003.png | Bin 1358 -> 0 bytes companion/src/images/library/00004.png | Bin 8265 -> 0 bytes companion/src/images/library/00005.png | Bin 5422 -> 0 bytes companion/src/images/library/00006.png | Bin 3097 -> 0 bytes companion/src/images/library/00007.png | Bin 3439 -> 0 bytes companion/src/images/library/00008.png | Bin 2434 -> 0 bytes companion/src/images/library/00009.png | Bin 2053 -> 0 bytes companion/src/images/library/00010.png | Bin 3215 -> 0 bytes companion/src/images/library/00011.png | Bin 2171 -> 0 bytes companion/src/images/library/00012.png | Bin 2474 -> 0 bytes companion/src/images/library/00021.png | Bin 7277 -> 0 bytes companion/src/images/library/00022.png | Bin 4962 -> 0 bytes companion/src/images/library/00050.png | Bin 353 -> 0 bytes companion/src/images/library/00051.png | Bin 3692 -> 0 bytes companion/src/images/library/00052.png | Bin 3469 -> 0 bytes companion/src/images/library/00053.png | Bin 425 -> 0 bytes companion/src/images/library/00054.png | Bin 341 -> 0 bytes companion/src/images/library/00055.png | Bin 325 -> 0 bytes companion/src/images/library/00056.png | Bin 603 -> 0 bytes companion/src/images/library/00057.png | Bin 634 -> 0 bytes companion/src/images/library/00058.png | Bin 3760 -> 0 bytes companion/src/images/library/00059.png | Bin 451 -> 0 bytes companion/src/images/library/00060.png | Bin 764 -> 0 bytes companion/src/images/library/00061.png | Bin 3151 -> 0 bytes companion/src/images/library/00062.png | Bin 3485 -> 0 bytes companion/src/images/library/00063.png | Bin 4665 -> 0 bytes companion/src/images/library/00064.png | Bin 3727 -> 0 bytes companion/src/images/library/00065.png | Bin 1040 -> 0 bytes companion/src/images/library/00066.png | Bin 368 -> 0 bytes companion/src/images/library/00304.png | Bin 865 -> 0 bytes companion/src/images/library/10801.png | Bin 725 -> 0 bytes companion/src/images/library/10802.png | Bin 881 -> 0 bytes companion/src/mainwindow.cpp | 70 +- companion/src/mainwindow.h | 7 +- companion/src/mdichild.cpp | 8 +- companion/src/process_flash.cpp | 336 ---- companion/src/process_flash.h | 74 - companion/src/progress/CMakeLists.txt | 30 + .../src/{ => progress}/progressdialog.cpp | 16 +- companion/src/{ => progress}/progressdialog.h | 1 - .../src/{ => progress}/progressdialog.ui | 0 .../src/{ => progress}/progresswidget.cpp | 1 - companion/src/{ => progress}/progresswidget.h | 0 .../src/{ => progress}/progresswidget.ui | 0 companion/src/radiointerface.cpp | 326 ---- companion/src/radiointerface.h | 46 - companion/src/radionotfound.cpp | 40 - companion/src/radionotfound.h | 43 - companion/src/radionotfound.ui | 117 -- companion/src/splash.h | 86 - companion/src/storage/appdata.cpp | 9 +- companion/src/storage/appdata.h | 23 +- companion/src/storage/firmwareinterface.cpp | 195 +- companion/src/storage/firmwareinterface.h | 52 +- .../thirdparty/qcustomplot/qcustomplot.cpp | 9 +- companion/src/updates/CMakeLists.txt | 2 + companion/src/updates/updatecloudbuild.cpp | 67 +- companion/src/updates/updatecloudbuild.h | 1 + companion/src/updates/updatefirmware.cpp | 19 +- companion/src/updates/updatenetwork.cpp | 13 +- companion/src/updates/updatenetwork.h | 3 + .../targets/linux/45-companion-taranis.rules | 1 - companion/targets/linux/45-usbasp.rules | 1 - 105 files changed, 2926 insertions(+), 3676 deletions(-) create mode 100644 cmake/FetchRsDfu.cmake delete mode 100644 cmake/FindDfuutil.cmake delete mode 100644 cmake/FindLibusb1.cmake delete mode 100644 companion/src/burnconfigdialog.cpp delete mode 100644 companion/src/burnconfigdialog.h delete mode 100644 companion/src/burnconfigdialog.ui create mode 100644 companion/src/flash/CMakeLists.txt rename companion/src/{ => flash}/customizesplashdialog.cpp (99%) rename companion/src/{ => flash}/customizesplashdialog.h (100%) rename companion/src/{ => flash}/customizesplashdialog.ui (100%) create mode 100644 companion/src/flash/flashfirmwaredialog.cpp rename companion/src/{ => flash}/flashfirmwaredialog.h (52%) rename companion/src/{ => flash}/flashfirmwaredialog.ui (72%) rename companion/src/{ => flash}/process_copy.cpp (100%) rename companion/src/{ => flash}/process_copy.h (100%) rename companion/src/{ => flash}/process_sync.cpp (100%) rename companion/src/{ => flash}/process_sync.h (100%) create mode 100644 companion/src/flash/radiointerface.cpp create mode 100644 companion/src/flash/radiointerface.h rename companion/src/{ => flash}/splashlabel.h (59%) rename companion/src/{ => flash}/splashlibrarydialog.cpp (81%) rename companion/src/{ => flash}/splashlibrarydialog.h (70%) rename companion/src/{ => flash}/splashlibrarydialog.ui (88%) delete mode 100644 companion/src/flashfirmwaredialog.cpp delete mode 100644 companion/src/fusesdialog.cpp delete mode 100644 companion/src/fusesdialog.h delete mode 100644 companion/src/fusesdialog.ui delete mode 100644 companion/src/images/library/00001.png delete mode 100644 companion/src/images/library/00002.png delete mode 100644 companion/src/images/library/00003.png delete mode 100644 companion/src/images/library/00004.png delete mode 100644 companion/src/images/library/00005.png delete mode 100644 companion/src/images/library/00006.png delete mode 100644 companion/src/images/library/00007.png delete mode 100644 companion/src/images/library/00008.png delete mode 100644 companion/src/images/library/00009.png delete mode 100644 companion/src/images/library/00010.png delete mode 100644 companion/src/images/library/00011.png delete mode 100644 companion/src/images/library/00012.png delete mode 100644 companion/src/images/library/00021.png delete mode 100644 companion/src/images/library/00022.png delete mode 100644 companion/src/images/library/00050.png delete mode 100644 companion/src/images/library/00051.png delete mode 100644 companion/src/images/library/00052.png delete mode 100644 companion/src/images/library/00053.png delete mode 100644 companion/src/images/library/00054.png delete mode 100644 companion/src/images/library/00055.png delete mode 100644 companion/src/images/library/00056.png delete mode 100644 companion/src/images/library/00057.png delete mode 100644 companion/src/images/library/00058.png delete mode 100644 companion/src/images/library/00059.png delete mode 100644 companion/src/images/library/00060.png delete mode 100644 companion/src/images/library/00061.png delete mode 100644 companion/src/images/library/00062.png delete mode 100644 companion/src/images/library/00063.png delete mode 100644 companion/src/images/library/00064.png delete mode 100644 companion/src/images/library/00065.png delete mode 100644 companion/src/images/library/00066.png delete mode 100644 companion/src/images/library/00304.png delete mode 100644 companion/src/images/library/10801.png delete mode 100644 companion/src/images/library/10802.png delete mode 100644 companion/src/process_flash.cpp delete mode 100644 companion/src/process_flash.h create mode 100644 companion/src/progress/CMakeLists.txt rename companion/src/{ => progress}/progressdialog.cpp (89%) rename companion/src/{ => progress}/progressdialog.h (98%) rename companion/src/{ => progress}/progressdialog.ui (100%) rename companion/src/{ => progress}/progresswidget.cpp (99%) rename companion/src/{ => progress}/progresswidget.h (100%) rename companion/src/{ => progress}/progresswidget.ui (100%) delete mode 100644 companion/src/radiointerface.cpp delete mode 100644 companion/src/radiointerface.h delete mode 100644 companion/src/radionotfound.cpp delete mode 100644 companion/src/radionotfound.h delete mode 100644 companion/src/radionotfound.ui delete mode 100644 companion/src/splash.h delete mode 100644 companion/targets/linux/45-companion-taranis.rules delete mode 100644 companion/targets/linux/45-usbasp.rules diff --git a/.github/workflows/macosx_cpn.yml b/.github/workflows/macosx_cpn.yml index 268275478..cdee31fcd 100644 --- a/.github/workflows/macosx_cpn.yml +++ b/.github/workflows/macosx_cpn.yml @@ -69,12 +69,6 @@ jobs: - name: Install dependencies run: python3 -m pip install --upgrade pip Pillow lz4 clang jinja2 - - name: Install libusb - run: brew install libusb - - - name: Install dfu-util - run: brew install dfu-util - - name: Install SDL2 run: brew install SDL2 diff --git a/.github/workflows/win_cpn-64.yml b/.github/workflows/win_cpn-64.yml index e5cbcd7ca..a73643d6a 100644 --- a/.github/workflows/win_cpn-64.yml +++ b/.github/workflows/win_cpn-64.yml @@ -71,46 +71,6 @@ jobs: echo $PATH $Python3_ROOT_DIR/python3.exe -m pip install clang jinja2 lz4 pillow - - name: Setup dfu-util and libusb - run: | - #!/bin/bash - set -e - - echo "Setting up dfu-util and libusb..." - - # Create target directory - mkdir -p dfu-util-0.11 - - # Download and extract dfu-util - echo "Downloading dfu-util binaries..." - curl -LO https://dfu-util.sourceforge.net/releases/dfu-util-0.11-binaries.tar.xz - tar -xf dfu-util-0.11-binaries.tar.xz - cp dfu-util-0.11-binaries/win64/dfu-util.exe dfu-util-0.11/ - - # Install 7zip if not available - if ! command -v 7z &> /dev/null; then - echo "Installing 7zip..." - sudo apt-get update -qq - sudo apt-get install -y p7zip-full - fi - - # Download and extract libusb - echo "Downloading libusb..." - curl -LO https://github.com/libusb/libusb/releases/download/v1.0.29/libusb-1.0.29.7z - 7z x -olibusb-1.0.29 libusb-1.0.29.7z > /dev/null - cp libusb-1.0.29/VS2022/MS64/dll/* dfu-util-0.11/ - - # Clean up temporary files - rm -f dfu-util-0.11-binaries.tar.xz libusb-1.0.29.7z - rm -rf dfu-util-0.11-binaries libusb-1.0.29 - - echo "Setup complete. Contents of dfu-util-0.11:" - ls -la dfu-util-0.11/ - - echo "LIBUSB1_ROOT_DIR=$(pwd)/dfu-util-0.11" >> $GITHUB_ENV - echo "DFU_UTIL_ROOT_DIR=$(pwd)/dfu-util-0.11" >> $GITHUB_ENV - - - name: Setup MSVC uses: ilammy/msvc-dev-cmd@v1 diff --git a/cmake/FetchMaxLibQt.cmake b/cmake/FetchMaxLibQt.cmake index ae57968a7..eb053608e 100644 --- a/cmake/FetchMaxLibQt.cmake +++ b/cmake/FetchMaxLibQt.cmake @@ -5,7 +5,7 @@ include(FetchContent) FetchContent_Declare( maxLibQt GIT_REPOSITORY https://github.com/edgetx/maxLibQt - GIT_TAG 98f5482fb8b836f48a6a5ee199911882fa420f2b + GIT_TAG 7e433da60d3f2e975d46afc91804a88029cd1b78 ) FetchContent_MakeAvailable(maxLibQt) diff --git a/cmake/FetchMiniz.cmake b/cmake/FetchMiniz.cmake index cbf4b56c6..0ed59446a 100644 --- a/cmake/FetchMiniz.cmake +++ b/cmake/FetchMiniz.cmake @@ -17,4 +17,4 @@ include_directories( ) # suppress compiler warnings for this add-in only -target_compile_options(miniz PRIVATE -Wno-unused-function) +target_compile_options(miniz PRIVATE -Wno-unused-function -Wno-newline-eof) diff --git a/cmake/FetchRsDfu.cmake b/cmake/FetchRsDfu.cmake new file mode 100644 index 000000000..5880a71f4 --- /dev/null +++ b/cmake/FetchRsDfu.cmake @@ -0,0 +1,40 @@ +# Fetch rs_dfu +cmake_minimum_required(VERSION 3.14) + +include(FetchContent) + +# Determine platform and architecture +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(RS_DFU_PLATFORM "unknown-linux-gnu") +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(RS_DFU_PLATFORM "apple-darwin") +elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(RS_DFU_PLATFORM "pc-windows-msvc") +else() + message(FATAL_ERROR "Unsupported platform: ${CMAKE_SYSTEM_NAME}") +endif() + +if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64") + set(RS_DFU_ARCH "x86_64") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64") + set(RS_DFU_ARCH "aarch64") +else() + message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +set(RS_DFU_BASE_URL "https://github.com/EdgeTX/rs-dfu/releases/latest/download") +set(RS_DFU_FILENAME "rs_dfu-${RS_DFU_ARCH}-${RS_DFU_PLATFORM}") +set(RS_DFU_URL "${RS_DFU_BASE_URL}/${RS_DFU_FILENAME}.tar.gz") + +FetchContent_Declare( + RsDfu + URL ${RS_DFU_URL} + DOWNLOAD_EXTRACT_TIMESTAMP true + FIND_PACKAGE_ARGS +) + +FetchContent_MakeAvailable(RsDfu) + +set(rs_dfu_DIR "${rsdfu_SOURCE_DIR}/cmake") + +message("Fetched rsdfu source code from Github: ${rsdfu_SOURCE_DIR}") diff --git a/cmake/FindDfuutil.cmake b/cmake/FindDfuutil.cmake deleted file mode 100644 index cf75d5d0c..000000000 --- a/cmake/FindDfuutil.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# - try to find program dfu-util - -set(DFU_UTIL_ROOT_DIR - "${DFU_UTIL_ROOT_DIR}" - CACHE - PATH - "Root directory to search for dfu-util") - -find_program(DFU_UTIL_PROGRAM - NAMES - dfu-util - HINTS - "${DFU_UTIL_ROOT_DIR}") - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(DFU_UTIL - DEFAULT_MSG - DFU_UTIL_PROGRAM) - -if(DFU_UTIL_FOUND) - set(DFU_UTIL_PATH "${DFU_UTIL_PROGRAM}") - mark_as_advanced(DFU_UTIL_ROOT_DIR) -endif() - -mark_as_advanced(DFU_UTIL_PROGRAM) diff --git a/cmake/FindLibusb1.cmake b/cmake/FindLibusb1.cmake deleted file mode 100644 index d63f22aae..000000000 --- a/cmake/FindLibusb1.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# - try to find libusb-1 library - -set(LIBUSB1_ROOT_DIR - "${LIBUSB1_ROOT_DIR}" - CACHE - PATH - "Root directory to search for libusb-1") - -find_library(LIBUSB1_LIBRARY - NAMES - libusb-1.0 - libusb-1.0.so - libusb-1.0.so.0 - libusb-1.0.dll - libusb-1.0.dylib - HINTS - "${LIBUSB1_ROOT_DIR}") - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LIBUSB1 - DEFAULT_MSG - LIBUSB1_LIBRARY) - -mark_as_advanced(LIBUSB1_LIBRARY) diff --git a/cmake/NativeTargets.cmake b/cmake/NativeTargets.cmake index 30b653a55..c89c375d3 100644 --- a/cmake/NativeTargets.cmake +++ b/cmake/NativeTargets.cmake @@ -13,27 +13,15 @@ else() endif() if(Qt6Core_FOUND AND NOT DISABLE_COMPANION) - # environment variable set in github workflows and build-edgetx Dockerfile - if(DEFINED ENV{LIBUSB1_ROOT_DIR}) - set(LIBUSB1_ROOT_DIR "$ENV{LIBUSB1_ROOT_DIR}") - endif() - - find_package(Libusb1) - - if(LIBUSB1_FOUND) - if(DEFINED ENV{DFU_UTIL_ROOT_DIR}) - set(DFU_UTIL_ROOT_DIR "$ENV{DFU_UTIL_ROOT_DIR}") - endif() - - find_package(Dfuutil) - endif() - # environment variable set in github workflows and build-edgetx Dockerfile if(DEFINED ENV{OPENSSL_ROOT_DIR}) set(OPENSSL_ROOT_DIR "$ENV{OPENSSL_ROOT_DIR}") endif() find_package(OpenSSL) + + include(FetchRsDfu) + find_package(rs_dfu REQUIRED) endif() # Windows-specific includes and libs shared by sub-projects diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index 3ee7f3d0f..04d23c6b1 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -81,6 +81,8 @@ include_directories( ${SIMU_SRC_DIRECTORY} ${COMPANION_SRC_DIRECTORY}/thirdparty/qcustomplot ${COMPANION_SRC_DIRECTORY}/storage + ${COMPANION_SRC_DIRECTORY}/flash + ${COMPANION_SRC_DIRECTORY}/progress ) ############# Do macro replacements on input files ############# @@ -175,6 +177,7 @@ add_library(common ${common_SRCS} ${common_RCC}) target_link_libraries(common datamodels firmwares + progress shared simulation storage @@ -196,8 +199,10 @@ include(FetchMaxLibQt) add_subdirectory(datamodels) add_subdirectory(firmwares) +add_subdirectory(flash) add_subdirectory(generaledit) add_subdirectory(modeledit) +add_subdirectory(progress) add_subdirectory(shared) add_subdirectory(simulation) add_subdirectory(storage) @@ -211,10 +216,7 @@ include_directories(${maxLibQt_SOURCE_DIR}/widgets) set(companion_NAMES apppreferencesdialog - burnconfigdialog comparedialog - customizesplashdialog - flashfirmwaredialog helpers_html labels logsdialog @@ -224,15 +226,7 @@ set(companion_NAMES modelslist multimodelprinter printdialog - process_copy - process_flash - process_sync profilechooser - progressdialog - progresswidget - radiointerface - radionotfound - splashlibrarydialog styleeditdialog dialogs/filesyncdialog ) @@ -254,11 +248,6 @@ set(companion_SRCS companion.cpp ) -set(companion_HDRS - ${companion_HDRS} - splashlabel.h -) - if(WIN32) set(icon_RC images/winicons/icon.rc) endif() @@ -277,6 +266,7 @@ target_link_libraries(${COMPANION_NAME} PRIVATE qcustomplot miniz updates + flash wizard ) @@ -363,15 +353,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps" RENAME "${COMPANION_NAME}.svg") - # udev rules - install(FILES "${COMPANION_TARGETS_DIR}/45-companion-taranis.rules" - DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/udev/rules.d" - RENAME "45-${COMPANION_NAME}-taranis.rules") - - install(FILES "${COMPANION_TARGETS_DIR}/45-usbasp.rules" - DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/udev/rules.d" - RENAME "45-${COMPANION_NAME}-usbasp.rules") - if(OPENSSL_FOUND) get_filename_component(OPENSSL_SSL_LIBRARY_DIR ${OPENSSL_SSL_LIBRARY} DIRECTORY) install(DIRECTORY ${OPENSSL_SSL_LIBRARY_DIR}/ @@ -381,18 +362,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") PATTERN "libssl.so.*") endif() - if(LIBUSB1_FOUND) - get_filename_component(LIBUSB1_LIBRARY_DIR ${LIBUSB1_LIBRARY} DIRECTORY) - install(DIRECTORY ${LIBUSB1_LIBRARY_DIR}/ - DESTINATION ${CMAKE_INSTALL_PREFIX}/lib - FILES_MATCHING PATTERN "libusb-1.0.so.*") - if(DFU_UTIL_FOUND) - # dfuutil is an app - install(FILES "${DFU_UTIL_PATH}" - DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) - endif() - endif() - set(OperatingSystem "Linux") # Shortcut target add_custom_target(companion DEPENDS ${COMPANION_NAME}) @@ -415,30 +384,6 @@ elseif(WIN32) install(FILES ${LICENSE_FILE} DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) - # supporting utilities - if(LIBUSB1_FOUND) - cmake_path(GET LIBUSB1_LIBRARY - PARENT_PATH - LIBUSB1_LIBRARY_DIR) - - if(MINGW) - # finds dll.a directory but we need dlls but should check extension just to be sure and future proof - set(LIBUSB1_LIBRARY_DIR "${LIBUSB1_LIBRARY_DIR}/../bin") - endif() - - set(LIBUSB1_LIBRARY_FILE "${LIBUSB1_LIBRARY_DIR}/libusb-1.0.dll") - - if(EXISTS "${LIBUSB1_LIBRARY_FILE}") - install(FILES "${LIBUSB1_LIBRARY_FILE}" - DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) - endif() - - if(DFU_UTIL_FOUND) - install(FILES "${DFU_UTIL_PATH}" - DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) - endif() - endif(LIBUSB1_FOUND) - if(OPENSSL_FOUND) cmake_path(GET OPENSSL_SSL_LIBRARY PARENT_PATH @@ -511,14 +456,6 @@ elseif(APPLE) install(FILES ${MAC_ICON_FILE} DESTINATION ${companion_res_dir} COMPONENT Runtime) - - if(DFU_UTIL_FOUND) - # Copy dfu-util, resolve symlink first - file(REAL_PATH "${DFU_UTIL_PATH}" DFU_UTIL_ABSOLUTE_PATH) - install(PROGRAMS ${DFU_UTIL_ABSOLUTE_PATH} - DESTINATION ${companion_res_dir} - COMPONENT Runtime) - endif(DFU_UTIL_FOUND) endif() ############# Packaging #################### @@ -605,7 +542,6 @@ elseif(APPLE) CONTENT " qt_deploy_runtime_dependencies( EXECUTABLE ${companion_app_dir} - ADDITIONAL_EXECUTABLES ${companion_res_dir}/dfu-util NO_TRANSLATIONS NO_COMPILER_RUNTIME GENERATE_QT_CONF diff --git a/companion/src/apppreferencesdialog.cpp b/companion/src/apppreferencesdialog.cpp index 611cb3135..c731260b6 100644 --- a/companion/src/apppreferencesdialog.cpp +++ b/companion/src/apppreferencesdialog.cpp @@ -56,6 +56,10 @@ AppPreferencesDialog::AppPreferencesDialog(QWidget * parent, UpdateFactories * f connect(ui->boardCB, SIGNAL(currentIndexChanged(int)), this, SLOT(onBaseFirmwareChanged())); connect(ui->opt_appDebugLog, &QCheckBox::toggled, this, &AppPreferencesDialog::toggleAppLogSettings); connect(ui->opt_fwTraceLog, &QCheckBox::toggled, this, &AppPreferencesDialog::toggleAppLogSettings); + connect(ui->backupPath, &QLineEdit::editingFinished, this, &AppPreferencesDialog::onBackupPathEditingFinished); + connect(ui->backupPathButton, &QPushButton::clicked, this, &AppPreferencesDialog::onBackupPathButtonClicked); + connect(ui->profileBackupPath, &QLineEdit::editingFinished, this, &AppPreferencesDialog::onProfileBackupPathEditingFinished); + connect(ui->profileBackupPathButton, &QPushButton::clicked, this, &AppPreferencesDialog::onProfileBackupPathButtonClicked); #if !defined(USE_SDL) ui->joystickCB->hide(); @@ -172,8 +176,8 @@ void AppPreferencesDialog::accept() profile.defaultMode(ui->stickmodeCB->currentData().toInt()); profile.burnFirmware(ui->burnFirmware->isChecked()); profile.sdPath(ui->sdPath->text()); - profile.pBackupDir(ui->profilebackupPath->text()); - profile.penableBackup(ui->pbackupEnable->isChecked()); + profile.pBackupDir(ui->profileBackupPath->text()); + profile.penableBackup(ui->profileBackupEnable->isChecked()); profile.splashFile(ui->SplashFileName->text()); profile.runSDSync(ui->chkPromptSDSync->isChecked()); profile.radioSimCaseColor(ui->lblRadioColorSample->palette().button().color()); @@ -243,7 +247,7 @@ void AppPreferencesDialog::on_btnRadioColor_clicked() void AppPreferencesDialog::initSettings() { - const Profile & profile = g.currentProfile(); + Profile & profile = g.currentProfile(); ui->burnFirmware->setChecked(profile.burnFirmware()); @@ -257,19 +261,15 @@ void AppPreferencesDialog::initSettings() ui->libraryPath->setText(g.libDir()); ui->ge_lineedit->setText(g.gePath()); - if (!g.backupDir().isEmpty()) { - if (QDir(g.backupDir()).exists()) { - ui->backupPath->setText(g.backupDir()); - ui->backupEnable->setEnabled(true); - ui->backupEnable->setChecked(g.enableBackup()); - } - else { - ui->backupEnable->setDisabled(true); - } - } - else { - ui->backupEnable->setDisabled(true); + ui->backupPath->setText(g.backupDir()); + if (!ui->backupPath->text().isEmpty() && QDir(ui->backupPath->text()).exists()) { + ui->backupEnable->setEnabled(true); + ui->backupEnable->setChecked(g.enableBackup()); + } else { + ui->backupEnable->setChecked(false); + ui->backupEnable->setEnabled(false); } + ui->splashincludeCB->setCurrentIndex(g.embedSplashes()); ui->opt_appDebugLog->setChecked(g.appDebugLog()); @@ -336,17 +336,22 @@ void AppPreferencesDialog::initSettings() ui->stickmodeCB->setModel(GeneralSettings::stickModeItemModel()); ui->stickmodeCB->setCurrentIndex(ui->stickmodeCB->findData(profile.defaultMode())); ui->sdPath->setText(profile.sdPath()); - if (!profile.pBackupDir().isEmpty()) { - if (QDir(profile.pBackupDir()).exists()) { - ui->profilebackupPath->setText(profile.pBackupDir()); - ui->pbackupEnable->setEnabled(true); - ui->pbackupEnable->setChecked(profile.penableBackup()); + + ui->profileBackupPath->setText(profile.pBackupDir()); + if (!ui->profileBackupPath->text().isEmpty()) { + if (QDir(ui->profileBackupPath->text()).exists()) { + ui->profileBackupEnable->setEnabled(true); + ui->profileBackupEnable->setChecked(profile.penableBackup()); } else { - ui->pbackupEnable->setDisabled(true); + ui->profileBackupEnable->setChecked(false); + ui->profileBackupEnable->setEnabled(false); } - } - else { - ui->pbackupEnable->setDisabled(true); + } else if (!ui->backupPath->text().isEmpty() && QDir(ui->backupPath->text()).exists()) { + ui->profileBackupEnable->setEnabled(true); + ui->profileBackupEnable->setChecked(profile.penableBackup()); + } else { + ui->profileBackupEnable->setChecked(false); + ui->profileBackupEnable->setEnabled(false); } if (Boards::isSurface()) { @@ -584,26 +589,33 @@ void AppPreferencesDialog::on_snapshotClipboardCKB_clicked() } } -void AppPreferencesDialog::on_backupPathButton_clicked() +void AppPreferencesDialog::onBackupPathButtonClicked() { - QString fileName = QFileDialog::getExistingDirectory(this,tr("Select your Models and Settings backup folder"), g.backupDir()); + QString fileName = QFileDialog::getExistingDirectory(this,tr("Select your global backup folder"), g.backupDir()); if (!fileName.isEmpty()) { g.backupDir(fileName); ui->backupPath->setText(fileName); ui->backupEnable->setEnabled(true); + ui->profileBackupEnable->setEnabled(true); + } else { + ui->backupEnable->setEnabled(false); + if (!g.currentProfile().pBackupDir().isEmpty() && QFileInfo(g.currentProfile().pBackupDir()).exists()) { + ui->profileBackupEnable->setEnabled(true); + } else { + ui->profileBackupEnable->setEnabled(false); + } } } -void AppPreferencesDialog::on_ProfilebackupPathButton_clicked() +void AppPreferencesDialog::onProfileBackupPathButtonClicked() { - QString fileName = QFileDialog::getExistingDirectory(this,tr("Select your Models and Settings backup folder"), g.backupDir()); + QString fileName = QFileDialog::getExistingDirectory(this,tr("Select your profile backup folder"), g.backupDir()); if (!fileName.isEmpty()) { - ui->profilebackupPath->setText(fileName); - ui->pbackupEnable->setEnabled(true); + ui->profileBackupPath->setText(fileName); + ui->profileBackupEnable->setEnabled(true); } } - void AppPreferencesDialog::on_btn_appLogsDir_clicked() { QString fileName = QFileDialog::getExistingDirectory(this, tr("Select a folder for application logs"), ui->appLogsDir->text()); @@ -857,3 +869,32 @@ void AppPreferencesDialog::shrink() { adjustSize(); } + +void AppPreferencesDialog::onBackupPathEditingFinished() +{ + if(!ui->backupPath->text().isEmpty() && QFileInfo(ui->backupPath->text()).exists()) { + ui->backupEnable->setEnabled(true); + } else { + ui->backupEnable->setChecked(false); + ui->backupEnable->setEnabled(false); + } + + onProfileBackupPathEditingFinished(); +} + +void AppPreferencesDialog::onProfileBackupPathEditingFinished() +{ + if (!ui->profileBackupPath->text().isEmpty()) { + if (QDir(ui->profileBackupPath->text()).exists()) { + ui->profileBackupEnable->setEnabled(true); + } else { + ui->profileBackupEnable->setChecked(false); + ui->profileBackupEnable->setEnabled(false); + } + } else if (!ui->backupPath->text().isEmpty() && QDir(ui->backupPath->text()).exists()) { + ui->profileBackupEnable->setEnabled(true); + } else { + ui->profileBackupEnable->setChecked(false); + ui->profileBackupEnable->setEnabled(false); + } +} diff --git a/companion/src/apppreferencesdialog.h b/companion/src/apppreferencesdialog.h index 7424868e5..51739af7b 100644 --- a/companion/src/apppreferencesdialog.h +++ b/companion/src/apppreferencesdialog.h @@ -64,8 +64,8 @@ class AppPreferencesDialog : public QDialog void on_libraryPathButton_clicked(); void on_snapshotPathButton_clicked(); void on_snapshotClipboardCKB_clicked(); - void on_backupPathButton_clicked(); - void on_ProfilebackupPathButton_clicked(); + void onBackupPathButtonClicked(); + void onProfileBackupPathButtonClicked(); void on_ge_pathButton_clicked(); void on_sdPathButton_clicked(); @@ -80,6 +80,8 @@ class AppPreferencesDialog : public QDialog #endif void on_btnRadioColor_clicked(); + void onBackupPathEditingFinished(); + void onProfileBackupPathEditingFinished(); private: void initSettings(); diff --git a/companion/src/apppreferencesdialog.ui b/companion/src/apppreferencesdialog.ui index f98d802c1..895f25ae7 100644 --- a/companion/src/apppreferencesdialog.ui +++ b/companion/src/apppreferencesdialog.ui @@ -26,17 +26,7 @@ true - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - + @@ -45,13 +35,540 @@ - 0 + 1 Radio Profile + + + + Simulator Case Colour + + + + + + + + 0 + 0 + + + + The profile specific folder, if set, will override general Backup folder + + + Backup folder + + + + + + + Default Int. Module + + + + + + + Qt::Orientation::Vertical + + + + 20 + 5 + + + + + + + + + 0 + 0 + + + + General Settings + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 5 + + + 4 + + + + + + + + Select Folder + + + + + + + + 0 + 0 + + + + Prompt to write firmware to radio after update + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + + + + 0 + 0 + + + + + true + + + + Splash Screen + + + + + + + true + + + true + + + true + + + + + + + Select Image + + + + + + + + 0 + 0 + + + + + 128 + 64 + + + + + 212 + 64 + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Plain + + + 1 + + + + + + true + + + + + + + Clear Image + + + + + + + + 0 + 0 + + + + Qt::Orientation::Horizontal + + + + + + + + + + + 16777215 + 16777215 + + + + Mode selection: + +Mode 1: + Left stick: Elevator, Rudder + Right stick: Throttle, Aileron + +Mode 2: + Left stick: Throttle, Rudder + Right stick: Elevator, Aileron + +Mode 3: + Left stick: Elevator, Aileron + Right stick: Throttle, Rudder + +Mode 4: + Left stick: Throttle, Aileron + Right stick: Elevator, Rudder + + + + + 1 + + + + Mode 1 (RUD ELE THR AIL) + + + + + Mode 2 (RUD THR ELE AIL) + + + + + Mode 3 (AIL ELE THR RUD) + + + + + Mode 4 (AIL THR ELE RUD) + + + + + + + + + + + + + + + 0 + 0 + + + + Qt::Orientation::Horizontal + + + + + + + + 0 + 0 + + + + Language + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + + + + + External Module + + + + + + + + 0 + 0 + + + + Default Stick Mode + + + + + + + + 0 + 0 + + + + Qt::Orientation::Horizontal + + + + + + + + 0 + 0 + + + + + true + + + + Other Settings + + + + + + + + 0 + 0 + + + + Default Channel Order + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + true + + + + + + false + + + true + + + + + + + + 0 + 0 + + + + Options + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + + + + + true + + + + + + + + + + The profile specific folder, if set, will override general Backup folder + + + Select Folder + + + + + + + + 0 + 0 + + + + + true + + + + Profile Name + + + + + + + Select Colour + + + + + + + true + + + + 0 + 0 + + + + General Settings Label + + + + + + + true + + + + + + + If set it will override the application general setting + + + false + + + true + + + + + + + Prompt to run SD Sync after update + + + + + + + + 0 + 0 + + + + if set, will override general backup enable + + + Prompt to backup current firmware before writing firmware + + + @@ -64,20 +581,7 @@ SD Structure path - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Prompt to write firmware to radio after update + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter @@ -217,457 +721,6 @@ - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 6 - - - - - - 0 - 0 - - - - - 75 - true - - - - Splash Screen - - - - - - - true - - - true - - - - - - - Select Image - - - - - - - - 0 - 0 - - - - - 128 - 64 - - - - - 212 - 64 - - - - QFrame::StyledPanel - - - QFrame::Plain - - - 1 - - - - - - true - - - - - - - Clear Image - - - - - - - - 0 - 0 - - - - Qt::Horizontal - - - - - - - - - - Prompt to run SD Sync after update - - - - - - - true - - - - 0 - 0 - - - - General Settings Label - - - - - - - - 0 - 0 - - - - Qt::Horizontal - - - - - - - - 16777215 - 16777215 - - - - Mode selection: - -Mode 1: - Left stick: Elevator, Rudder - Right stick: Throttle, Aileron - -Mode 2: - Left stick: Throttle, Rudder - Right stick: Elevator, Aileron - -Mode 3: - Left stick: Elevator, Aileron - Right stick: Throttle, Rudder - -Mode 4: - Left stick: Throttle, Aileron - Right stick: Elevator, Rudder - - - - - 1 - - - - Mode 1 (RUD ELE THR AIL) - - - - - Mode 2 (RUD THR ELE AIL) - - - - - Mode 3 (AIL ELE THR RUD) - - - - - Mode 4 (AIL THR ELE RUD) - - - - - - - - Default Int. Module - - - - - - - - 0 - 0 - - - - if set, will override general backup enable - - - Enable automatic backup before writing firmware - - - - - - - The profile specific folder, if set, will override general Backup folder - - - Select Folder - - - - - - - - - - If set it will override the application general setting - - - false - - - - - - - - 0 - 0 - - - - General Settings - - - - - - - - 0 - 0 - - - - Default Channel Order - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Profile Name - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Other Settings - - - - - - - - - - - 0 - 0 - - - - Language - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Select Folder - - - - - - - - - - - 0 - 0 - - - - Options - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - - 0 - 0 - - - - Default Stick Mode - - - - - - - - 0 - 0 - - - - - - - - External Module - - - - - - - - 0 - 0 - - - - The profile specific folder, if set, will override general Backup folder - - - Backup folder - - - - - - - - 0 - 0 - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 5 - - - 4 - - - - @@ -678,7 +731,6 @@ Mode 4: - 75 true @@ -686,64 +738,32 @@ Mode 4: Radio Type - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter - - + + + + + 0 + 0 + + - Qt::Vertical - - - - 20 - 5 - - - - - - - - true - - - - - - false + Qt::Orientation::Horizontal - - - - true - - - - - - - Simulator Case Colour - - - - - - - true + + + + + true + - - - - - - - - Select Colour + Updates @@ -759,6 +779,20 @@ Mode 4: 6 + + + + Prompt to run installer after update + + + + + + + Qt::Orientation::Horizontal + + + @@ -772,186 +806,7 @@ Mode 4: - - - - Update - - - - - - - Prompt to run installer after update - - - - - - - - - true - - - - - - - - 0 - 0 - - - - Select Executable - - - - - - - - - - - <html><head/><body><p>Keep a log of all debugging messages generated by the desktop Companion/Simulator applications. An EdgeTX developer may request this to help diagnose an issue.</p></body></html> - - - Application (Companion/Simulator) - - - - - - - <html><head/><body><p>Keep a log of all messages generated by the radio firmware when running in Simulator. This is the same information one would also see in the Simulator <span style=" font-style:italic;">Debug Output</span> window.</p></body></html> - - - Radio Firmware (in Simulator) - - - - - - - - - - 0 - 0 - - - - Google Earth Executable - - - - - - - - 0 - 0 - - - - Debug Output Logging - - - - - - - - - - - 0 - 0 - - - - Automatic Backup Folder - - - - - - - - - false - - - - - - - - 0 - 0 - - - - Select Folder - - - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - Output Logs Folder - - - - - - - Show splash screen - - - true - - - - + @@ -971,7 +826,144 @@ Mode 4: - + + + + + 0 + 0 + + + + Google Earth Executable + + + + + + + Show splash screen + + + true + + + + + + + Qt::Orientation::Horizontal + + + + + + + + 0 + 0 + + + + Splash Screen Library + + + + + + + Radio Profiles + + + + + + + Qt::Orientation::Horizontal + + + + + + + Move selected Radio Profile to the top of the list + + + + + + + + + true + + + true + + + + + + + + 0 + 0 + + + + Select Executable + + + + + + + + + <html><head/><body><p>This option maintains the behaviour from older OpenTx versions where empty model slots are preserved when a model is deleted or moved. </p><p>When this option is de-selected, the other models may be re-arranged to fill the gap left by the removed model.</p></body></html> + + + Remove empty model slots when deleting models (only applies for radios w/out labels) + + + + + + + + + + + 0 + 0 + + + + Action on New Model + + + + + + + + 0 + 0 + + + + Startup Settings + + + + + + + Update + + + + @@ -981,6 +973,9 @@ Mode 4: false + + true + @@ -998,8 +993,8 @@ Mode 4: - - + + 0 @@ -1007,12 +1002,12 @@ Mode 4: - Action on New Model + Output Logs Folder - - + + 0 @@ -1020,29 +1015,36 @@ Mode 4: - User Splash Screens + Backup Folder - - - - - 0 - 0 - - - - Enable automatic backup before writing firmware - - + + - - - - Prompt for radio profile - - + + + + + + true + + + false + + + true + + + + + + + Select Folder + + + + @@ -1080,63 +1082,80 @@ Mode 4: - - - - - 0 - 0 - - - - Splash Screen Library - - - - - - - - 0 - 0 - - - - Startup Settings - - - - - + + - Qt::Horizontal + Qt::Orientation::Horizontal - - - - <html><head/><body><p>This option maintains the behaviour from older OpenTx versions where empty model slots are preserved when a model is deleted or moved. </p><p>When this option is de-selected, the other models may be re-arranged to fill the gap left by the removed model.</p></body></html> + + + + + + <html><head/><body><p>Keep a log of all debugging messages generated by the desktop Companion/Simulator applications. An EdgeTX developer may request this to help diagnose an issue.</p></body></html> + + + Application (Companion/Simulator) + + + + + + + <html><head/><body><p>Keep a log of all messages generated by the radio firmware when running in Simulator. This is the same information one would also see in the Simulator <span style=" font-style:italic;">Debug Output</span> window.</p></body></html> + + + Radio Firmware (in Simulator) + + + + + + + + + + 0 + 0 + - Remove empty model slots when deleting models (only applies for radios w/out labels) + User Splash Screens - - + + + + + 0 + 0 + + + + Prompt to backup current firmware before writing new firmware + + + + + - + true false + + true + - + Select Folder @@ -1144,24 +1163,30 @@ Mode 4: - - - - Radio Profiles - - - - - - - Move selected Radio Profile to the top of the list - - - - - + + - Qt::Horizontal + Qt::Orientation::Horizontal + + + + + + + + 0 + 0 + + + + Debug Output Logging + + + + + + + Prompt for radio profile @@ -1170,7 +1195,7 @@ Mode 4: - Qt::Vertical + Qt::Orientation::Vertical @@ -1217,12 +1242,15 @@ Mode 4: true + + true + - Qt::Vertical + Qt::Orientation::Vertical @@ -1326,7 +1354,7 @@ Mode 4: BackLight Color - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter @@ -1443,7 +1471,7 @@ Mode 4: - QComboBox::AdjustToContents + QComboBox::SizeAdjustPolicy::AdjustToContents @@ -1482,7 +1510,7 @@ Mode 4: - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1515,7 +1543,11 @@ Mode 4: - + + + true + + @@ -1539,6 +1571,9 @@ Mode 4: false + + true + @@ -1563,6 +1598,9 @@ Mode 4: false + + true + @@ -1625,7 +1663,7 @@ Mode 4: - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1651,7 +1689,7 @@ Mode 4: - Qt::Vertical + Qt::Orientation::Vertical @@ -1665,6 +1703,16 @@ Mode 4: + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok + + + @@ -1672,9 +1720,8 @@ Mode 4: boardCB sdPath sdPathButton - profilebackupPath - ProfilebackupPathButton - pbackupEnable + profileBackupPath + profileBackupPathButton stickmodeCB channelorderCB burnFirmware @@ -1688,7 +1735,6 @@ Mode 4: joystickChkB joystickcalButton volumeGain - buttonBox diff --git a/companion/src/burnconfigdialog.cpp b/companion/src/burnconfigdialog.cpp deleted file mode 100644 index 7f79de707..000000000 --- a/companion/src/burnconfigdialog.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "burnconfigdialog.h" -#include "ui_burnconfigdialog.h" -#include "eeprominterface.h" -#include "helpers.h" -#include "appdata.h" -#include "progressdialog.h" -#include "process_flash.h" - -#if !defined _MSC_VER && defined __GNUC__ -#include -#endif - -burnConfigDialog::burnConfigDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::burnConfigDialog) -{ - ui->setupUi(this); - setWindowIcon(CompanionIcon("configure.png")); - - getSettings(); - Board::Type board = getCurrentBoard(); - if (IS_STM32(board)) { - setWindowTitle(tr("DFU-UTIL Configuration")); - ui->label_sb1->hide(); - ui->label_sb3->hide(); - ui->samba_location->hide(); - ui->samba_port->hide(); - ui->sb_browse->hide(); - } - else { - setWindowTitle(tr("SAM-BA Configuration")); - ui->label_dfu1->hide(); - ui->dfu_location->hide(); - ui->dfu_browse->hide(); - } - ui->label_sb2->hide(); - ui->arm_mcu->hide(); - ui->label_dfu2->hide(); - ui->dfuArgs->hide(); - - QTimer::singleShot(0, this, SLOT(shrink())); - connect(this, SIGNAL(accepted()), this, SLOT(putSettings())); -} - -burnConfigDialog::~burnConfigDialog() -{ - delete ui; -} - - -void burnConfigDialog::getSettings() -{ - sambaLoc = g.sambaLocation(); - dfuLoc = g.dfuLocation(); - -#if defined WIN32 || !defined __GNUC__ - if ( sambaLoc.isEmpty()) - sambaLoc = QFileInfo("sam-ba.exe").absoluteFilePath(); - if ( dfuLoc.isEmpty()) - dfuLoc = QFileInfo("bin/dfu-util.exe").absoluteFilePath(); -#elif defined __APPLE__ - if ( sambaLoc.isEmpty()) - sambaLoc = "/usr/local/bin/sam-ba"; - if ( dfuLoc.isEmpty()) - dfuLoc = QFileInfo(QApplication::applicationDirPath() + "/../Resources/dfu-util").absoluteFilePath(); -#elif defined __FreeBSD__ - if (dfuLoc.isEmpty()) - dfuLoc = "/usr/local/bin/dfu-util"; -#else - if ( sambaLoc.isEmpty()) - sambaLoc = "/usr/bin/sam-ba"; - if ( dfuLoc.isEmpty()) - dfuLoc = QFileInfo("/usr/bin/dfu-util").absoluteFilePath(); -#endif - - dfuArgs = g.dfuArguments().split(" ", Qt::SkipEmptyParts); - armMCU = g.armMcu(); - sambaPort = g.sambaPort(); - - ui->samba_location->setText(getSAMBA()); - ui->samba_port->setText(getSambaPort()); - - ui->dfu_location->setText(getDFU()); - ui->dfuArgs->setText(getDFUArgs().join(" ")); - - int idx4 = ui->arm_mcu->findText(getArmMCU()); - if(idx4>=0) ui->arm_mcu->setCurrentIndex(idx4); - QFile file; -} - -void burnConfigDialog::putSettings() -{ - g.sambaLocation( sambaLoc ); - g.sambaPort( sambaPort ); - g.armMcu( armMCU ); - g.dfuLocation( dfuLoc ); - g.dfuArguments( dfuArgs.join(" ") ); -} - -void burnConfigDialog::on_samba_location_editingFinished() -{ - sambaLoc = ui->samba_location->text(); -} - -void burnConfigDialog::on_samba_port_editingFinished() -{ - sambaPort = ui->samba_port->text(); -} - -void burnConfigDialog::on_arm_mcu_currentIndexChanged(QString ) -{ - armMCU = ui->arm_mcu->currentText(); -} - -void burnConfigDialog::on_sb_browse_clicked() -{ - QString fileName = QFileDialog::getOpenFileName(this, tr("Select Location"),ui->samba_location->text()); - if(!fileName.isEmpty()) - { - ui->samba_location->setText(fileName); - sambaLoc = fileName; - } -} - -void burnConfigDialog::on_dfu_browse_clicked() -{ - QString fileName = QFileDialog::getOpenFileName(this, tr("Select Location"),ui->dfu_location->text()); - - if(!fileName.isEmpty()) - { - ui->dfu_location->setText(fileName); - dfuLoc = fileName; - } -} - -void burnConfigDialog::on_dfu_location_editingFinished() -{ - dfuLoc = ui->dfu_location->text(); -} - -void burnConfigDialog::on_dfuArgs_editingFinished() -{ - dfuArgs = ui->dfuArgs->text().split(" ", Qt::SkipEmptyParts); -} - -void burnConfigDialog::on_advCtrChkB_toggled(bool checked) -{ - Board::Type board = getCurrentBoard(); - if (checked) { - if (IS_STM32(board)) { - ui->label_dfu2->show(); - ui->dfuArgs->show(); - } - else { - ui->label_sb2->show(); - ui->arm_mcu->show(); - } - } - else { - if (IS_STM32(board)) { - ui->label_dfu2->hide(); - ui->dfuArgs->hide(); - } - else { - ui->label_sb2->hide(); - ui->arm_mcu->hide(); - } - } - QTimer::singleShot(0, this, SLOT(shrink())); -} - - -void burnConfigDialog::shrink() -{ - resize(0, 0); -} - - - diff --git a/companion/src/burnconfigdialog.h b/companion/src/burnconfigdialog.h deleted file mode 100644 index d52b10213..000000000 --- a/companion/src/burnconfigdialog.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _BURNCONFIGDIALOG_H_ -#define _BURNCONFIGDIALOG_H_ - -#include - -#define MEM_TYPE_EEPROM 1 -#define MEM_TYPE_FLASH 2 - -#define OPR_TYPE_READ 1 -#define OPR_TYPE_WRITE 2 - -namespace Ui { - class burnConfigDialog; -} - -class burnConfigDialog : public QDialog { - Q_OBJECT -public: - burnConfigDialog(QWidget *parent = 0); - ~burnConfigDialog(); - - QString getSAMBA() {return sambaLoc;} - QString getDFU() {return dfuLoc;} - QStringList getDFUArgs() {return dfuArgs;} - QString getArmMCU() {return armMCU;} - QString getSambaPort() {return sambaPort;} - -private: - Ui::burnConfigDialog *ui; - - void populateProgrammers(); - - QString sambaLoc; - QString dfuLoc; - QString armMCU; - QString sambaPort; - QStringList dfuArgs; - -private slots: - void shrink(); - void on_samba_location_editingFinished(); - void on_samba_port_editingFinished(); - void on_arm_mcu_currentIndexChanged(QString ); - void on_sb_browse_clicked(); - void on_dfu_browse_clicked(); - void on_dfu_location_editingFinished(); - void on_dfuArgs_editingFinished(); - void on_advCtrChkB_toggled(bool checked); - void getSettings(); - void putSettings(); -}; - -#endif // _BURNCONFIGDIALOG_H_ diff --git a/companion/src/burnconfigdialog.ui b/companion/src/burnconfigdialog.ui deleted file mode 100644 index e52aaf797..000000000 --- a/companion/src/burnconfigdialog.ui +++ /dev/null @@ -1,271 +0,0 @@ - - - burnConfigDialog - - - - 0 - 0 - 531 - 422 - - - - - 0 - 0 - - - - - 531 - 0 - - - - Programmer Configuration - - - - :/icon.png:/icon.png - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - false - - - - - - - - - CPU of your TX - - - CPU present on your 9x radio -Should be m64 for stock radios -m2560 for v4.1 boards - - - QComboBox::InsertAlphabetically - - - - at91sam3s4-9x - - - - - at91sam3s8-9xr - - - - - - - - Location of sam-ba executable - - - The location of the AVRDUDE executable. - - - dfu-util - - - - - - - DFU-Util Location - - - - - - - SAM-BA Location - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Location of sam-ba executable - - - The location of the AVRDUDE executable. - - - sam-ba.exe - - - - - - - ARM MCU - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Port - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - sam-ba serial port - - - The location of the AVRDUDE executable. - - - \USBserial\COM23 - - - - - - - Alternate device - - - - - - - Extra arguments that will be passed to AVRDUDE on every call - - - Extra arguments used in AVRDUDE. -This can be used for providing extra information to AVRDUDE. - -Please only use this if you know what you are doing. There are no error checks and you could cripple your controller. - - - 0 - - - - - - - Use this button to browse and look for the AVRDUDE executable file. - - - Browse... - - - - - - - Use this button to browse and look for the AVRDUDE executable file. - - - Browse... - - - - - - - - - - - - - - Use advanced controls - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - samba_location - sb_browse - arm_mcu - samba_port - dfu_location - dfu_browse - dfuArgs - advCtrChkB - buttonBox - - - - - - - buttonBox - rejected() - burnConfigDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - buttonBox - accepted() - burnConfigDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - diff --git a/companion/src/companion.qrc b/companion/src/companion.qrc index 8712699d3..053b0f2f0 100644 --- a/companion/src/companion.qrc +++ b/companion/src/companion.qrc @@ -69,18 +69,6 @@ images/wizard/throttle.png images/wizard/vtail.png images/wizard/wingtype.png - images/library/00001.png - images/library/00002.png - images/library/00003.png - images/library/00004.png - images/library/00005.png - images/library/00006.png - images/library/00007.png - images/library/00008.png - images/library/00009.png - images/library/00010.png - images/library/00011.png - images/library/00012.png images/library/00013.png images/library/00014.png images/library/00015.png @@ -89,25 +77,6 @@ images/library/00018.png images/library/00019.png images/library/00020.png - images/library/00021.png - images/library/00022.png - images/library/00050.png - images/library/00051.png - images/library/00052.png - images/library/00053.png - images/library/00054.png - images/library/00055.png - images/library/00056.png - images/library/00057.png - images/library/00058.png - images/library/00059.png - images/library/00060.png - images/library/00061.png - images/library/00062.png - images/library/00063.png - images/library/00064.png - images/library/00065.png - images/library/00066.png images/library/00100.png images/library/00101.png images/library/00102.png @@ -122,7 +91,6 @@ images/library/00301.png images/library/00302.png images/library/00303.png - images/library/00304.png images/library/00401.png images/library/00402.png images/library/00403.png @@ -144,8 +112,6 @@ images/library/10601.png images/library/10701.png images/library/10702.png - images/library/10801.png - images/library/10802.png themes/default/style.css themes/default/style-osx.css themes/default/modelprt.css diff --git a/companion/src/constants.h b/companion/src/constants.h index 003783ea3..50bfcfc5e 100644 --- a/companion/src/constants.h +++ b/companion/src/constants.h @@ -67,18 +67,14 @@ #define CPN_STR_FILES QCoreApplication::translate("Companion", "files") #define CPN_STR_RAD_MOD_SETTINGS QCoreApplication::translate("Companion", "Radio and Models settings") -#define HEX_FILES_FILTER "HEX " % CPN_STR_FILES % " (*.hex);;" #define BIN_FILES_FILTER "BIN " % CPN_STR_FILES % " (*.bin);;" -#define DFU_FILES_FILTER "DFU " % CPN_STR_FILES % " (*.dfu);;" -#define EEPE_FILES_FILTER "EEPE " % CPN_STR_FILES % " (*.eepe);;" +#define UF2_FILES_FILTER "UF2 " % CPN_STR_FILES % " (*.uf2);;" #define ETX_FILES_FILTER "EdgeTX " % CPN_STR_FILES % " (*.etx);;" #define YML_FILES_FILTER "EdgeTX YAML " % CPN_STR_FILES % " (*.yml);;" #define EEPROM_FILES_FILTER CPN_STR_RAD_MOD_SETTINGS % " " % CPN_STR_FILES % " (*.etx *.yml);;" % ETX_FILES_FILTER % YML_FILES_FILTER -#define FLASH_FILES_FILTER "FLASH " % CPN_STR_FILES % " (*.bin *.hex *.dfu);;" % BIN_FILES_FILTER % HEX_FILES_FILTER % DFU_FILES_FILTER -#define EXTERNAL_EEPROM_FILES_FILTER "EEPROM " % CPN_STR_FILES % " (*.bin *.hex);;" % BIN_FILES_FILTER % HEX_FILES_FILTER -#define ER9X_EEPROM_FILE_TYPE "ER9X_EEPROM_FILE" +#define FIRMWARE_FILES_FILTER "Firmware " % CPN_STR_FILES % " (*.bin *.uf2);;" % BIN_FILES_FILTER % UF2_FILES_FILTER +#define EXTERNAL_EEPROM_FILES_FILTER "EEPROM " % CPN_STR_FILES % " (*.bin);;" % BIN_FILES_FILTER #define EEPE_EEPROM_FILE_HEADER "EEPE EEPROM FILE" -#define EEPE_MODEL_FILE_HEADER "EEPE MODEL FILE" #define SIMU_FILES_FILTER ETX_FILES_FILTER #define CPN_STR_APP_SETTINGS QCoreApplication::translate("Companion", "Application Settings") diff --git a/companion/src/flash/CMakeLists.txt b/companion/src/flash/CMakeLists.txt new file mode 100644 index 000000000..bbccca403 --- /dev/null +++ b/companion/src/flash/CMakeLists.txt @@ -0,0 +1,42 @@ + +project(flash) + +set(${PROJECT_NAME}_NAMES + customizesplashdialog + flashfirmwaredialog + process_copy + process_sync + radiointerface + splashlibrarydialog +) + +AddHeadersSources() + +# these have no cpp +set(${PROJECT_NAME}_HDRS + ${${PROJECT_NAME}_HDRS} + splashlabel.h +) + +qt_wrap_cpp(${PROJECT_NAME}_SRCS +) + +add_library(${PROJECT_NAME} + ${${PROJECT_NAME}_HDRS} + ${${PROJECT_NAME}_SRCS} +) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + ${CPN_COMMON_LIB} + ${RS_DFU_LIBRARIES} +) + +target_include_directories(${PROJECT_NAME} + PRIVATE + "${CMAKE_SOURCE_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}" + PUBLIC + "${CMAKE_CURRENT_LIST_DIR}" + "${RS_DFU_INCLUDE_DIRS}" +) diff --git a/companion/src/customizesplashdialog.cpp b/companion/src/flash/customizesplashdialog.cpp similarity index 99% rename from companion/src/customizesplashdialog.cpp rename to companion/src/flash/customizesplashdialog.cpp index 191928063..c7b589bb3 100644 --- a/companion/src/customizesplashdialog.cpp +++ b/companion/src/flash/customizesplashdialog.cpp @@ -202,12 +202,11 @@ void CustomizeSplashDialog::on_copyLeftToRightButton_clicked() { right.copyImage(left); } - void CustomizeSplashDialog::on_leftLoadFwButton_clicked() {loadFirmware(left);} void CustomizeSplashDialog::on_rightLoadFwButton_clicked() {loadFirmware(right);} void CustomizeSplashDialog::loadFirmware(Side side) { - QString fileName = QFileDialog::getOpenFileName(this, tr("Open Firmware File"), g.flashDir(), FLASH_FILES_FILTER); + QString fileName = QFileDialog::getOpenFileName(this, tr("Open Firmware File"), g.flashDir(), FIRMWARE_FILES_FILTER); if (!fileName.isEmpty()) { if (!side.displayImage( fileName, FW )) QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Can not load embedded image from firmware file %1.").arg(fileName)); diff --git a/companion/src/customizesplashdialog.h b/companion/src/flash/customizesplashdialog.h similarity index 100% rename from companion/src/customizesplashdialog.h rename to companion/src/flash/customizesplashdialog.h diff --git a/companion/src/customizesplashdialog.ui b/companion/src/flash/customizesplashdialog.ui similarity index 100% rename from companion/src/customizesplashdialog.ui rename to companion/src/flash/customizesplashdialog.ui diff --git a/companion/src/flash/flashfirmwaredialog.cpp b/companion/src/flash/flashfirmwaredialog.cpp new file mode 100644 index 000000000..143766c77 --- /dev/null +++ b/companion/src/flash/flashfirmwaredialog.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "flashfirmwaredialog.h" +#include "ui_flashfirmwaredialog.h" +#include "appdata.h" +#include "helpers.h" +#include "radiointerface.h" +#include "firmwareinterface.h" +#include "progressdialog.h" +#include "progresswidget.h" +#include "splashlibrarydialog.h" +#include "storage.h" + +FlashFirmwareDialog::FlashFirmwareDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::FlashFirmwareDialog), + fwName(g.profile[g.id()].fwName()) +{ + ui->setupUi(this); + + if (!g.currentProfile().splashFile().isEmpty()){ + imageSource = PROFILE; + imageFile = g.currentProfile().splashFile(); + ui->useProfileSplash->setChecked(true); + } else { + imageSource = FIRMWARE; + imageFile = ""; + ui->useProfileSplash->setDisabled(true); + } + + ui->checkHardwareCompatibility->setChecked(g.checkHardwareCompatibility()); + ui->checkBackup->setChecked(g.backupOnFlash()); + + detectClicked(true); + updateUI(); + + connect(ui->detectButton, &QPushButton::clicked, this, &FlashFirmwareDialog::detectClicked); + connect(ui->loadButton, &QPushButton::clicked, this, &FlashFirmwareDialog::loadClicked); + connect(ui->useProfileSplash, &QRadioButton::clicked, this, &FlashFirmwareDialog::useProfileSplashClicked); + connect(ui->useFirmwareSplash, &QRadioButton::clicked, this, &FlashFirmwareDialog::useFirmwareSplashClicked); + connect(ui->useLibrarySplash, &QRadioButton::clicked, this, &FlashFirmwareDialog::useLibrarySplashClicked); + connect(ui->useExternalSplash, &QRadioButton::clicked, this, &FlashFirmwareDialog::useExternalSplashClicked); + connect(ui->writeButton, &QPushButton::clicked, this, &FlashFirmwareDialog::writeButtonClicked); + connect(ui->cancelButton, &QPushButton::clicked, [this]() { close(); } ); +} + +FlashFirmwareDialog::~FlashFirmwareDialog() +{ + delete ui; +} + +void FlashFirmwareDialog::updateUI() +{ + ui->firmwareFilename->setText(fwName); + + if (connectionMode) { + ui->loadButton->setEnabled(true); + ui->writeButton->setEnabled(isFileConnectionCompatible() && QFile(fwName).exists()); + } else { + ui->loadButton->setEnabled(false); + ui->writeButton->setEnabled(false); + } + + FirmwareInterface firmware(fwName); + if (firmware.isValid()) { + ui->firmwareInfoFrame->show(); + ui->date->setText(firmware.getDate() + " " + firmware.getTime()); + ui->version->setText(firmware.getVersion()); + ui->variant->setText(firmware.getFlavour()); + ui->date->setEnabled(true); + ui->version->setEnabled(true); + ui->variant->setEnabled(true); + + if (firmware.hasSplash()) { + ui->splashFrame->show(); + ui->splash->setFixedSize(firmware.getSplashWidth(), firmware.getSplashHeight()); + } else { + ui->splashFrame->hide(); + } + } else { + imageSource = FIRMWARE; + ui->firmwareInfoFrame->hide(); + ui->splashFrame->hide(); + } + + QImage image; + switch (imageSource) { + case FIRMWARE: + ui->useFirmwareSplash->setChecked(true); + image = firmware.getSplash(); + break; + case PROFILE: + ui->useProfileSplash->setChecked(true); + image.load(g.currentProfile().splashFile()); + break; + case LIBRARY: + ui->useLibrarySplash->setChecked(true); + image.load(imageFile); + break; + case EXTERNAL: + ui->useExternalSplash->setChecked(true); + image.load(imageFile); + break; + } + + if (!image.isNull()) + ui->splash->setPixmap(makePixMap(image)); + + QTimer::singleShot(0, [=]() { adjustSize(); }); +} + +void FlashFirmwareDialog::loadClicked() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Open Firmware File"), + g.flashDir(), + getFirmwareFilesFilter()); + if (!fileName.isEmpty()) { + if (!firmwareFileExtensions().contains(QFileInfo(fileName).suffix().toLower())) { + QMessageBox::critical(this, tr("Open Firmware File"), + tr("%1 has an unsupported file extension") + .arg(fileName)); + return; + } + + FirmwareInterface fw(fileName); + + if (!fw.isValid()) { + QMessageBox::critical(this, tr("Open Firmware File"), + tr("%1 is not a valid firmware file") + .arg(fileName)); + return; + } + + if (connectionMode == CONNECTION_UF2) { + const Uf2Info uf2(getUf2Info()); + if (fw.getFlavour() != uf2.board) { + QMessageBox::critical(this, tr("Open Firmware File"), + tr("%1 \nFirmware file is for %2 radio however connected radio is %3") + .arg(fileName) + .arg(fw.getFlavour()) + .arg(uf2.board)); + return; + } + } + + if (fw.getFlavour() != getCurrentFirmware()->getFlavour()) { + QMessageBox::critical(this, tr("Open Firmware File"), + tr("%1 \nFirmware file is for %2 radio however profile is for %3 radio") + .arg(fileName) + .arg(fw.getFlavour()) + .arg(getCurrentFirmware()->getFlavour())); + return; + } + + fwName = fileName; + updateUI(); + } +} + +void FlashFirmwareDialog::useFirmwareSplashClicked() +{ + FirmwareInterface firmware(fwName); + + if (!firmware.isValid()) { + QMessageBox::warning(this, CPN_STR_TTL_ERROR, tr( "The firmware file is not valid." )); + } else if (!firmware.hasSplash()) { + QMessageBox::warning(this, CPN_STR_TTL_ERROR, + tr( "There is no start screen image in the firmware file." )); + } else { + imageSource = FIRMWARE; + } + + updateUI(); +} + +void FlashFirmwareDialog::useProfileSplashClicked() +{ + QString fileName = g.currentProfile().splashFile(); + + if (!fileName.isEmpty()) { + QImage image(fileName); + if (image.isNull()) { + QMessageBox::critical(this, CPN_STR_TTL_ERROR, + tr("Profile image %1 is invalid.").arg(fileName)); + } else { + imageSource = PROFILE; + } + } + + updateUI(); +} + +void FlashFirmwareDialog::useExternalSplashClicked() +{ + QString supportedImageFormats; + + for (int formatIndex = 0; formatIndex < QImageReader::supportedImageFormats().count(); formatIndex++) { + supportedImageFormats += QLatin1String(" *.") + QImageReader::supportedImageFormats()[formatIndex]; + } + + QString fileName = QFileDialog::getOpenFileName(this, tr("Open image file to use as radio start screen"), + g.imagesDir(), + tr("Images (%1)").arg(supportedImageFormats)); + + if (!fileName.isEmpty()){ + g.imagesDir( QFileInfo(fileName).dir().absolutePath() ); + QImage image(fileName); + + if (image.isNull()) { + QMessageBox::critical(this, CPN_STR_TTL_ERROR, + tr("Image could not be loaded from %1") + .arg(fileName)); + } else { + imageSource = EXTERNAL; + imageFile = fileName; + } + } + + updateUI(); +} + +void FlashFirmwareDialog::useLibrarySplashClicked() +{ + QString fileName; + SplashLibraryDialog(this, &fileName).exec(); + + if (!fileName.isEmpty()) { + QImage image(fileName); + + if (image.isNull()) { + QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("The library image could not be loaded")); + } else { + imageSource = LIBRARY; + imageFile = fileName; + } + } + + updateUI(); +} + +void FlashFirmwareDialog::writeButtonClicked() +{ + g.flashDir(QFileInfo(fwName).dir().absolutePath()); + g.currentProfile().fwName(fwName); + g.checkHardwareCompatibility(ui->checkHardwareCompatibility->isChecked()); + g.backupOnFlash(ui->checkBackup->isChecked()); + + if (imageSource != FIRMWARE) { + // load the splash image + const QPixmap pixmap = ui->splash->pixmap(Qt::ReturnByValue); + QImage image; + image = pixmap.toImage().scaled(ui->splash->width(), ui->splash->height()); + + if (image.isNull()) { + QMessageBox::critical(this, CPN_STR_TTL_WARNING, tr("Splash image not found")); + return; + } + + // write the customized firmware + QString tempFile; + tempFile = generateProcessUniqueTempFileName("flash.bin"); + qDebug() << "patching" << fwName << "with custom splash screen and saving to" << tempFile; + FirmwareInterface firmware(fwName); + firmware.setSplash(image); + + if (firmware.save(tempFile) <= 0) { + QMessageBox::critical(this, CPN_STR_TTL_WARNING, tr("Cannot save customised firmware")); + return; + } + + startWrite(tempFile); + } else { + startWrite(fwName); + } +} + +void FlashFirmwareDialog::startWrite(const QString &filename) +{ + close(); + + ProgressDialog progressDialog(this, tr("Flash Firmware to Radio"), + CompanionIcon("write_flash.png")); + + auto progress = progressDialog.progress(); + progress->addMessage(tr("Flashing with file: %1").arg(filename)); + bool checkHw = g.checkHardwareCompatibility(); + bool backup = g.backupOnFlash(); + FirmwareInterface newfw(filename); + + if (g.checkHardwareCompatibility() || backup) { + readFirmware( + [this, &newfw, progress, checkHw, backup](const QByteArray &_data) { + FirmwareInterface currfw(_data); + if (!currfw.isValid()) { + QString errMsg(tr("Firmware read from radio invalid")); + progress->addMessage(errMsg, QtFatalMsg); + progress->setInfo(errMsg); + return; + } + + if (backup) { + if (!writeFirmwareToFile(this, _data, progress)) { + return; + } + } else { + progress->addMessage(tr("Backup of old firmware not requested")); + } + + if (checkHw) { + if (!newfw.isHardwareCompatible(currfw)) { + QString errMsg(tr("New firmware is not compatible")); + progress->addMessage(errMsg, QtFatalMsg); + progress->setInfo(errMsg); + return; + } else { + progress->addMessage(tr("New firmware is compatible")); + } + } else { + progress->addMessage(tr("New firmware compatibity check not requested")); + } + + progress->addMessage(tr("Start flashing firmware")); + writeFirmware(newfw.getFlash(), progress); + }, + [this, progress](const QString &err) { + progress->addMessage(tr("Could not read current firmware: %1").arg(err)); + }, + progress); + } else { + progress->addMessage(tr("Start flashing firmware (no backup or compatiblity check)")); + writeFirmware(newfw.getFlash(), progress); + } + + progressDialog.exec(); + + if (isTempFileName(filename)) { + qDebug() << "Removing temporary file" << filename; + qunlink(filename); + } +} + +void FlashFirmwareDialog::detectClicked(bool atLoad) +{ + QString connectionMsg; + + if (isUf2DeviceFound()) { + connectionMode = CONNECTION_UF2; + connectionMsg = tr("Radio connection mode: UF2"); + } else if (isDfuDeviceFound()) { + connectionMode = CONNECTION_DFU; + connectionMsg = tr("Radio connection mode: DFU"); + } else { + connectionMode = CONNECTION_NONE; + connectionMsg = tr("ALERT: No radio connected"); + } + + ui->connectionMode->setText(connectionMsg); + + if (!connectionMode) { + QMessageBox::warning((atLoad ? (QWidget *)parent(): this), tr("Detect Radio"), + tr("Radio could not be detected by DFU or UF2 modes") % ".\n" % + tr("Check cable is securely connected and radio lights are illuminated") % ".\n" % + tr("Note: USB mode is not suitable for flashing firmware.")); + } + + if (!isFileConnectionCompatible()) + fwName.clear(); + + if (!atLoad) + updateUI(); +} + +bool FlashFirmwareDialog::isFileConnectionCompatible() +{ + if (!fwName.isEmpty()) { + const QString extn(QFileInfo(fwName).suffix().toLower()); + if ((connectionMode == CONNECTION_DFU && dfuFileExtensions().contains(extn)) || + (connectionMode == CONNECTION_UF2 && uf2FileExtensions().contains(extn))) { + return true; + } + } + + return false; +} diff --git a/companion/src/flashfirmwaredialog.h b/companion/src/flash/flashfirmwaredialog.h similarity index 52% rename from companion/src/flashfirmwaredialog.h rename to companion/src/flash/flashfirmwaredialog.h index 307e19352..e066fe478 100644 --- a/companion/src/flashfirmwaredialog.h +++ b/companion/src/flash/flashfirmwaredialog.h @@ -19,12 +19,9 @@ * GNU General Public License for more details. */ -#ifndef _FLASHFIRMWAREDIALOG_H_ -#define _FLASHFIRMWAREDIALOG_H_ +#pragma once #include -#include "eeprominterface.h" -#include "firmwareinterface.h" namespace Ui { @@ -33,33 +30,33 @@ namespace Ui class FlashFirmwareDialog : public QDialog { - Q_OBJECT + Q_OBJECT -public: - FlashFirmwareDialog(QWidget *parent = 0); - ~FlashFirmwareDialog(); + public: + FlashFirmwareDialog(QWidget *parent = 0); + ~FlashFirmwareDialog(); -private slots: - void on_firmwareLoad_clicked(); - void on_burnButton_clicked(); - void on_cancelButton_clicked(); - void on_useProfileSplash_clicked(); - void on_useFirmwareSplash_clicked(); - void on_useLibrarySplash_clicked(); - void on_useExternalSplash_clicked(); - void shrink(); + private slots: + void detectClicked(bool atLoad = false); + void loadClicked(); + void writeButtonClicked(); + void useProfileSplashClicked(); + void useFirmwareSplashClicked(); + void useLibrarySplashClicked(); + void useExternalSplashClicked(); -protected: - void updateUI(); - void startFlash(const QString &filename); + protected: + void updateUI(); + void startWrite(const QString &filename); -private: - Ui::FlashFirmwareDialog *ui; - QString fwName; - RadioData radioData; - enum ImageSource {FIRMWARE, PROFILE, LIBRARY, EXTERNAL}; - ImageSource imageSource; - QString imageFile; + private: + enum ConnectionModes { CONNECTION_NONE, CONNECTION_UF2, CONNECTION_DFU }; + enum ImageSource {FIRMWARE, PROFILE, LIBRARY, EXTERNAL}; + + Ui::FlashFirmwareDialog *ui; + QString fwName; + ImageSource imageSource; + QString imageFile; + int connectionMode; + bool isFileConnectionCompatible(); }; - -#endif // _FLASHFIRMWAREDIALOG_H_ diff --git a/companion/src/flashfirmwaredialog.ui b/companion/src/flash/flashfirmwaredialog.ui similarity index 72% rename from companion/src/flashfirmwaredialog.ui rename to companion/src/flash/flashfirmwaredialog.ui index 01d74b78c..0e24198b6 100644 --- a/companion/src/flashfirmwaredialog.ui +++ b/companion/src/flash/flashfirmwaredialog.ui @@ -7,7 +7,7 @@ 0 0 421 - 375 + 390 @@ -26,12 +26,45 @@ Flash Firmware - + :/icon.png:/icon.png + + + + + 0 + 0 + + + + Radio connection: Unknown + + + + + + + + 0 + 0 + + + + Detect the boot mode of the connected radio + + + Detect + + + + + + + @@ -55,13 +88,19 @@ - + + + false + 0 0 + + Select and load a firmware file. The file extension must be one suitable for the boot mode. + Load... @@ -78,10 +117,10 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -93,20 +132,26 @@ + + Firmware build version + true - + - Variant + Radio + + Target radio for which the firmware was built + true @@ -121,6 +166,9 @@ + + Buid timestamp + true @@ -138,12 +186,12 @@ - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised - + @@ -197,10 +245,10 @@ - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -225,30 +273,27 @@ - Allows Companion to write to older version of the firmware + Checks the new firmware matches the connected radio. If not the process will be aborted. - Check Hardware compatibility + Check hardware compatibility (recommended) - - - true + + + Read the connected radio firmware and write to the backup folder. - Backup and restore Models and Settings - - - false + Backup radio firmware before flashing - Qt::Vertical + Qt::Orientation::Vertical @@ -263,7 +308,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -287,7 +332,7 @@ - + false @@ -297,6 +342,9 @@ 0 + + Performing optional processes and write the loaded file to the conneced radio. + Write to TX @@ -307,15 +355,15 @@ - burnButton + writeButton firmwareFilename - firmwareLoad + loadButton version variant cancelButton - + diff --git a/companion/src/process_copy.cpp b/companion/src/flash/process_copy.cpp similarity index 100% rename from companion/src/process_copy.cpp rename to companion/src/flash/process_copy.cpp diff --git a/companion/src/process_copy.h b/companion/src/flash/process_copy.h similarity index 100% rename from companion/src/process_copy.h rename to companion/src/flash/process_copy.h diff --git a/companion/src/process_sync.cpp b/companion/src/flash/process_sync.cpp similarity index 100% rename from companion/src/process_sync.cpp rename to companion/src/flash/process_sync.cpp diff --git a/companion/src/process_sync.h b/companion/src/flash/process_sync.h similarity index 100% rename from companion/src/process_sync.h rename to companion/src/flash/process_sync.h diff --git a/companion/src/flash/radiointerface.cpp b/companion/src/flash/radiointerface.cpp new file mode 100644 index 000000000..9cdb84afd --- /dev/null +++ b/companion/src/flash/radiointerface.cpp @@ -0,0 +1,832 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "radiointerface.h" + +#include "appdata.h" +#include "progresswidget.h" +#include "storage.h" +#include "helpers.h" +#include "firmwareinterface.h" + +#include +#include + +#include +#include + +#define TR(msg) QCoreApplication::translate("RadioInterface", msg) + +constexpr int UF2_TRANSFER_SIZE {4 * 1024}; +constexpr char UF2_FIRMWARE_FILENAME[] {"CURRENT.UF2"}; +constexpr char UF2_INFO_FILENAME[] {"INFO_UF2.TXT"}; +constexpr char UF2_INDEX_FILENAME[] {"INDEX.HTM"}; +constexpr char UF2_NEW_FIRMWARE_FILENAME[] {"firmware.uf2"}; + +using namespace std::chrono_literals; + +FirmwareReaderWorker::FirmwareReaderWorker(QObject *parent) : + QThread(parent) +{ +} + +FirmwareReaderWorker::~FirmwareReaderWorker() +{ + //qDebug() << "FirmwareReaderWorker destructor called"; +} + +void FirmwareReaderWorker::run() +{ + isUf2DeviceFound() ? runUf2() : runDfu(); +} + +void FirmwareReaderWorker::runDfu() +{ + try { + auto device_filter = DfuDeviceFilter::empty_filter(); + auto devices = device_filter->find_devices(); + + if (devices.empty()) { + throw std::runtime_error(tr("No DFU devices found").toStdString()); + } else if (devices.size() > 1) { + throw std::runtime_error(tr("More than one DFU device found").toStdString()); + } + + emit statusChanged("Resetting state..."); + auto &device = devices[0]; + device.reset_state(); + + auto addr = device.default_start_address(); + auto ctx = device.start_upload(addr, 0); + + size_t total_length = ctx->get_length(); + size_t xfer_size = ctx->get_transfer_size(); + size_t bytes_uploaded = 0; + + QByteArray data; + while (bytes_uploaded < total_length) { + auto rest_length = uint32_t(total_length - bytes_uploaded); + auto single_xfer_size = std::min(uint32_t(xfer_size), rest_length); + bytes_uploaded += single_xfer_size; + + emit progressChanged(bytes_uploaded, total_length); + emit statusChanged(tr("Reading %1 of %2").arg(bytes_uploaded).arg(total_length)); + data.append(ctx->upload(single_xfer_size)); + } + + qDebug() << "Reading done"; + emit statusChanged("Reading done"); + emit complete(data); + + // TODO: handle shouldStop + + } catch (const std::exception &e) { + emit error(QString("DFU failed: %1").arg(e.what())); + } +} + +void FirmwareReaderWorker::runUf2() +{ + try { + QString path = findMassStoragePath(UF2_FIRMWARE_FILENAME); + QStorageInfo si(path); + QFile fw(path); + char buf[UF2_TRANSFER_SIZE]; + const int blockstotal = (fw.size() + UF2_TRANSFER_SIZE - 1) / UF2_TRANSFER_SIZE; + emit progressChanged(0, blockstotal); + emit statusChanged(tr("Reading...")); + + qDebug() << "Reading started - file:" << fw.fileName() + << "size:" << fw.size() + << "blocks:" << blockstotal; + + QByteArray data; + if (fw.open(QIODevice::ReadOnly)) { + int blockcnt = 0; + qint64 bytesread = 0; + int read = fw.read(buf, UF2_TRANSFER_SIZE); + if (read == 0 && fw.size() > 0) { + throw std::runtime_error(tr("Error reading %1 (reason: no data read)") + .arg(fw.fileName()).toStdString()); + } else if (read < 0) { + throw std::runtime_error(tr("Error reading %1 (reason: %2)") + .arg(fw.fileName()) + .arg(fw.errorString()).toStdString()); + } + + do + { + // qDebug() << "read:" << read; + bytesread += read; + blockcnt++; + data.append(buf, read); + emit progressChanged(blockcnt, blockstotal); + emit statusChanged(tr("Read block %1 of %2").arg(blockcnt).arg(blockstotal)); + read = fw.read(buf, UF2_TRANSFER_SIZE); + } while (read > 0); + + if (read < 0) { + data.clear(); + throw std::runtime_error(tr("Error reading %1 (reason: %2)") + .arg(fw.fileName()) + .arg(fw.errorString()).toStdString()); + } else if (bytesread < fw.size()) { + data.clear(); + throw std::runtime_error(tr("Error reading %1 (reason: read %2 of %3)") + .arg(fw.fileName()) + .arg(bytesread) + .arg(fw.size()).toStdString()); + } + + } else { + throw std::runtime_error(tr("Cannot open %1 (reason: %2)") + .arg(fw.fileName()) + .arg(fw.errorString()).toStdString()); + } + + fw.close(); + qDebug() << "Reading done"; + emit statusChanged("Reading done"); + emit complete(data); + + } catch (const std::exception &e) { + emit error(QString("UF2 failed: %1").arg(e.what())); + } +} + +FirmwareWriterWorker::FirmwareWriterWorker(const QByteArray &data, QObject *parent) : + QThread(parent), + firmwareData(data) +{ +} + +FirmwareWriterWorker::~FirmwareWriterWorker() +{ + //qDebug() << "FirmwareWriterWorker destructor called"; +} + +void FirmwareWriterWorker::run() +{ + isUf2DeviceFound() ? runUf2() : runDfu(); +} + +void FirmwareWriterWorker::runUf2() +{ + try { + QString path = findMassStoragePath(UF2_FIRMWARE_FILENAME, true); + if (path.isEmpty()) { + throw std::runtime_error(tr("Cannot find USB device containing %1") + .arg(UF2_FIRMWARE_FILENAME).toStdString()); + } + + QStorageInfo si(path); + if (si.bytesAvailable() < firmwareData.size()) { + throw std::runtime_error(tr("Insufficient free space on %1 to write new firmware") + .arg(QDir::toNativeSeparators(path)).toStdString()); + } + + QFile newfw(Helpers::concatPath(path, UF2_NEW_FIRMWARE_FILENAME)); + int blockstotal = (firmwareData.size() + UF2_TRANSFER_SIZE - 1) / UF2_TRANSFER_SIZE; + emit progressChanged(0, blockstotal); + emit statusChanged(tr("Writing...")); + + if (firmwareData.size() == 0) + throw std::runtime_error(tr("No data to write to new firmware file").toStdString()); + + qDebug() << "Writing started - file:" + << newfw.fileName() + << "size:" << firmwareData.size() + << "blocks:" << blockstotal; + + if (newfw.open(QIODevice::ReadWrite)) { + int blockcnt = 0; + qint64 byteswritten = 0; + QByteArray ba = firmwareData.mid(blockcnt * UF2_TRANSFER_SIZE, UF2_TRANSFER_SIZE); + + do + { + qint64 written = newfw.write(ba, ba.size()); + if (written < 0) { + throw std::runtime_error(tr("Error writing %1 (reason: %2)") + .arg(QDir::toNativeSeparators(newfw.fileName())) + .arg(newfw.errorString()).toStdString()); + } else if (written < ba.size()) { + throw std::runtime_error(tr("Error writing block to %1 (reason: bytes written %2 of %3)") + .arg(QDir::toNativeSeparators(newfw.fileName())) + .arg(written) + .arg(ba.size()).toStdString()); + } + + newfw.flush(); + byteswritten += written; + blockcnt++; + emit progressChanged(blockcnt, blockstotal); + emit statusChanged(tr("Writing block %1 of %2").arg(blockcnt).arg(blockstotal)); + + ba.clear(); // just to be on the safe side + ba = firmwareData.mid(blockcnt * UF2_TRANSFER_SIZE, UF2_TRANSFER_SIZE); + } while (ba.size() > 0); + + if (byteswritten < firmwareData.size()) { + throw std::runtime_error(tr("Error writing %1 (reason: written %2 of %3)") + .arg(QDir::toNativeSeparators(newfw.fileName())) + .arg(byteswritten) + .arg(firmwareData.size()).toStdString()); + } + + } else { + throw std::runtime_error(tr("Cannot open %1 (reason: %2)") + .arg(QDir::toNativeSeparators(newfw.fileName())) + .arg(newfw.errorString()).toStdString()); + } + + newfw.close(); + qDebug() << "Writing done"; + emit statusChanged("Writing done"); + emit complete(); + + } catch (const std::exception &e) { + emit error(QString("UF2 failed: %1").arg(e.what())); + } +} + +void FirmwareWriterWorker::runDfu() +{ + try { + auto device_filter = DfuDeviceFilter::empty_filter(); + auto devices = device_filter->find_devices(); + + if (devices.empty()) { + throw std::runtime_error(tr("No DFU devices found").toStdString()); + } else if (devices.size() > 1) { + throw std::runtime_error(tr("More than one DFU device found").toStdString()); + } + + emit statusChanged("Resetting state..."); + auto &device = devices[0]; + device.reset_state(); + + SliceU8 buffer_slice((const uint8_t *)firmwareData.constData(), + firmwareData.size()); + + if (!is_uf2_payload(buffer_slice)) { + auto addr = device.default_start_address(); + writeRegion(device, addr, buffer_slice); + } else { + writeUf2(device, buffer_slice); + } + + device.leave(); + + emit statusChanged("Flashing done"); + emit complete(); + + // TODO: handle shouldStop + + } catch (const std::exception &e) { + emit error(QString("DFU failed: %1").arg(e.what())); + } +} + +void FirmwareWriterWorker::writeUf2(DfuDevice &device, const SliceU8 &data) +{ + auto range_it = UF2RangeIterator::from_slice(data); + auto addr_range = UF2AddressRange::new_empty(); + + while (range_it->next(*addr_range)) { + auto addr = addr_range->start_address(); + const auto &payload = addr_range->payload(); + + uint32_t reboot_address = 0; + + if (addr_range->reboot_address(reboot_address)) { + rebootAndRediscover(device, addr, payload, reboot_address, 30s); + } else { + writeRegion(device, addr, payload); + } + } +} + +void FirmwareWriterWorker::writeRegion(const DfuDevice &device, uint32_t addr, + const SliceU8 &data) +{ + auto start_address = addr; + auto end_address = start_address + data.size() - 1; + + auto ctx = device.start_download(start_address, end_address); + auto erase_pages = ctx->get_erase_pages(); + auto pages = erase_pages.size(); + + for (size_t i = 0; i < pages; i++) { + updateEraseStatus(i + 1, pages); + ctx->page_erase(erase_pages[i]); + } + + size_t bytes_downloaded = 0; + auto data_ptr = data.data(); + auto xfer_size = ctx->get_transfer_size(); + + while (bytes_downloaded < data.size()) { + auto single_xfer_size = + std::min(uint32_t(xfer_size), uint32_t(data.size() - bytes_downloaded)); + bytes_downloaded += single_xfer_size; + updateDownloadStatus(bytes_downloaded, data.size()); + ctx->download(addr, SliceU8(data_ptr, single_xfer_size)); + addr += single_xfer_size; + data_ptr += single_xfer_size; + } +} + +void FirmwareWriterWorker::updateEraseStatus(size_t page, size_t pages) +{ + emit progressChanged(page, pages); + emit statusChanged(tr("Erasing page %1 of %2").arg(page).arg(pages)); +} + +void FirmwareWriterWorker::updateDownloadStatus(size_t bytes, size_t total) +{ + emit progressChanged(bytes, total); + emit statusChanged(tr("Writing %1 of %2").arg(bytes).arg(total)); +} + +template +void FirmwareWriterWorker::rebootAndRediscover(DfuDevice &device, uint32_t addr, + const SliceU8 &data, + uint32_t reboot_addr, + Duration timeout) +{ + emit statusChanged(tr("Rebooting into DFU...")); + emit progressChanged(0, 2); + + device.reboot(addr, data, reboot_addr); + emit progressChanged(1, 2); + + emit statusChanged(tr("Waiting for device to reconnect...")); + + auto start = std::chrono::steady_clock::now(); + + while (std::chrono::steady_clock::now() - start < timeout) { + if (device.rediscover()) { + emit statusChanged(tr("Device reconnected")); + emit progressChanged(2, 2); + return; + } + } + + throw std::runtime_error( + tr("Timeout while reconnecting to device").toStdString()); +} + +template +void connectProgress(Worker *worker, ProgressWidget *progress) +{ + // lock the progress dialog for as long as the thread is running + progress->connect(worker, &QThread::started, progress, + [progress]() { progress->lock(true); }); + + progress->connect(worker, &QThread::finished, progress, + [progress]() { progress->lock(false); }); + + // delete worker once the thread is finished + progress->connect(worker, &QThread::finished, worker, &QObject::deleteLater); + + // progress and status handling + progress->connect(worker, &Worker::progressChanged, progress, + [progress](int val, int max) { + if (progress->maximum() != max) + progress->setMaximum(max); + progress->setValue(val); + }); + + progress->connect(worker, &Worker::statusChanged, progress, + [progress](const QString& status) { + progress->addMessage(status); + progress->setInfo(status); + }); + + progress->connect(worker, &Worker::error, progress, + [progress](const QString& status) { + progress->addMessage(status, QtFatalMsg); + progress->setInfo(status); + }); +} + +bool readFirmware(const std::function& onComplete, + const std::function& onError, + ProgressWidget *progress) +{ + auto worker = std::make_unique(); + connectProgress(worker.get(), progress); + + // connect closures + progress->connect(worker.get(), &FirmwareReaderWorker::complete, progress, onComplete); + progress->connect(worker.get(), &FirmwareReaderWorker::error, progress, onError); + + // now start it! + worker.release()->start(); + return true; +} + +bool readFirmware(const std::function& onComplete, + ProgressWidget *progress) +{ + auto worker = std::make_unique(progress); + connectProgress(worker.get(), progress); + + // write data to file on success + progress->connect(worker.get(), &FirmwareReaderWorker::complete, progress, onComplete); + + // now start it! + worker.release()->start(); + return true; +} + +bool readFirmware(const QString &filename, ProgressWidget *progress) +{ + QFile::remove(filename); + return readFirmware( + [filename](const QByteArray &data) { + QFile file(filename); + if (!file.open(QFile::WriteOnly)) { + qDebug() << "Unable to write to " << filename; + } else { + file.write(data); + file.close(); + qDebug() << "Firmware written to " << filename; + } + }, + progress); +} + +bool readFirmware(QByteArray &data, ProgressWidget *progress) +{ + return readFirmware([&data](const QByteArray &_data) { data = _data; }, + progress); +} + +bool writeFirmware(const QString &filename, ProgressWidget *progress) +{ + QFile f(filename); + if (!f.open(QIODeviceBase::ReadOnly)) { + QMessageBox::critical(nullptr, TR("Write Firmware"), + TR("Cannot open file '%1': %2") + .arg(QDir::toNativeSeparators(filename)) + .arg(f.errorString())); + } + + auto data = f.readAll(); + f.close(); + + return writeFirmware(data, progress); +} + +bool writeFirmware(const QByteArray &data, ProgressWidget *progress) +{ + auto worker = std::make_unique(data, progress); + connectProgress(worker.get(), progress); + + // now start it! + worker.release()->start(); + return true; +} + +QString printDevicesInfo(const Vec &devices) +{ + QString ret; + for (const auto &device : devices) { + auto dev_info = device.device_info(); + ret.append(QString("%1:%2: %3 (%4)\n").arg(QString::number(dev_info.vendor_id, 16)) + .arg(QString::number(dev_info.product_id, 16)) + .arg(dev_info.product_string.c_str()) + .arg(QString::number(device.default_start_address(), 16))); + } + + return ret; +} + +QString getDevicesInfo() +{ + try { + auto device_filter = DfuDeviceFilter::empty_filter(); + auto devices = device_filter->find_devices(); + if (devices.empty()) { + return QString("No DFU devices found"); + } + + return printDevicesInfo(devices); + + } catch (const std::exception& e) { + return QString("Error: %1").arg(e.what()); + } + + return QString(); +} + +bool readSettings(const QString &filename, ProgressWidget *progress) +{ + QFile file(filename); + if (file.exists() && !file.remove()) { + QMessageBox::warning(nullptr, CPN_STR_TTL_ERROR, + TR("Could not delete temporary file: %1") + .arg(QDir::toNativeSeparators(filename))); + return false; + } + + return readSettingsSDCard(filename, progress); +} + +bool readSettingsSDCard(const QString &filename, ProgressWidget *progress, + bool fromRadio) +{ + QString radioPath; + if (fromRadio) { + radioPath = findMassStoragePath("RADIO", true, progress); + qDebug() << "Searching for SD card, found" << radioPath; + } else { + radioPath = g.currentProfile().sdPath(); + if (!QFile::exists(radioPath % "/RADIO")) + radioPath.clear(); + } + + if (radioPath.isEmpty()) { + QMessageBox::critical(progress, CPN_STR_TTL_ERROR, TR("Unable to find SD card!")); + return false; + } + + RadioData radioData; + Storage inputStorage(radioPath); + + if (!inputStorage.load(radioData)) { + QString errorMsg = inputStorage.error(); + if (errorMsg.isEmpty()) + errorMsg = TR("Failed to read Models and Settings from") + % QDir::toNativeSeparators(radioPath); + + QMessageBox::critical(progress, CPN_STR_TTL_ERROR, errorMsg); + return false; + } + + Storage outputStorage(filename); + if (!outputStorage.write(radioData)) { + QMessageBox::critical(progress, CPN_STR_TTL_ERROR, + TR("Failed to write Models and Setting file") % " " + % QDir::toNativeSeparators(filename)); + return false; + } + + return QFileInfo(filename).exists(); +} + +QString findMassStoragePath(const QString &filename, bool onlyPath, ProgressWidget *progress) +{ + QString foundPath; + QString foundProbefile; + int found = 0; + // Linux: "vfat"; macOS: "msdos" or "lifs"; Win: "FAT32" + QRegularExpression fstypeRe("^(v?fat|msdos|lifs)", + QRegularExpression::CaseInsensitiveOption); + + foreach (const QStorageInfo &si, QStorageInfo::mountedVolumes()) { + if (!si.isReady() || si.isReadOnly() || !QString(si.fileSystemType()).contains(fstypeRe)) + continue; + + qDebug() << "device:" << si.device() + << "type:" << si.fileSystemType() + << "block size:" << si.blockSize() + << "capacity:" << si.bytesTotal() / 1000 / 1000 << "MB" + << "available:" << si.bytesFree() / 1000/ 1000 << "MB" + << "root path:" << si.rootPath(); + + QString temppath = si.rootPath(); + QString probefile = Helpers::concatPath(temppath, filename); + qDebug() << "Searching for" << probefile; + if (QFile::exists(probefile)) { + found++; + foundPath = temppath; + foundProbefile = probefile; + qDebug() << probefile << "found"; + } + } + + if (found == 1) { + return onlyPath ? foundPath : foundProbefile; + } else if (found > 1) { + QMessageBox::critical(progress, CPN_STR_TTL_ERROR, QDir::toNativeSeparators(filename) + % " " % TR("found in multiple locations")); + } + + return QString(); +} + +QString getFirmwareFilesFilter() +{ + return isUf2DeviceFound() ? QString(UF2_FILES_FILTER) : QString(FIRMWARE_FILES_FILTER); +} + +Uf2Info getUf2Info() +{ + Uf2Info info; + QString path = findMassStoragePath(UF2_INFO_FILENAME); + if (path.isEmpty()) + return info; + + QFile file(path); + if (!file.open(QFile::ReadOnly)) { + QMessageBox::critical(nullptr, CPN_STR_TTL_ERROR, + TR("Error opening file %1:\n%2.") + .arg(QDir::toNativeSeparators(path)).arg(file.errorString())); + return info; + } + + QByteArray filedata; + const char *version_label = "UF2 Bootloader"; + const char *board_label = "Board-ID:"; + const char *date_label = "Date:"; + + filedata = file.readLine(); + + do + { + if (filedata.startsWith(version_label)) + info.version = filedata.mid(QString(version_label).size()).trimmed(); + else if (filedata.startsWith(board_label)) + info.board = filedata.mid(QString(board_label).size()).trimmed(); + else if (filedata.startsWith(date_label)) + info.date = filedata.mid(QString(date_label).size()).trimmed(); + + filedata = file.readLine(); + + } while (!filedata.isEmpty()); + + file.close(); + + qDebug() << "UF2 information - version:" << info.version + << "board:" << info.board + << "date:" << info.date; + + return info; +} + +bool isUf2DeviceFound() +{ + return !findMassStoragePath("INFO_UF2.TXT", false).isEmpty(); +} + +bool isDfuDeviceFound() +{ + try { + auto device_filter = DfuDeviceFilter::empty_filter(); + auto devices = device_filter->find_devices(); + if (devices.empty()) { + return false; + } + } catch (const std::exception& e) { + return false; + } + + return true; +} + +bool isRadioConnected() +{ + return isUf2DeviceFound() || isDfuDeviceFound(); +} + +bool writeFirmwareToFile(QWidget *parent, const QByteArray &data, ProgressWidget *progress, bool promptForFile) +{ + QString msg(TR("Writing radio firmware to file starting...")); + progress->setInfo(msg); + progress->addMessage(msg); + + FirmwareInterface fw(data); + if (!fw.isValid()) { + QString errMsg(TR("Firmware is not invalid")); + progress->addMessage(errMsg, QtFatalMsg); + progress->setInfo(errMsg); + return false; + } + + QString backupDir; + + if (g.currentProfile().getVariantFromType() == fw.getFlavour()) { + progress->addMessage(TR("Current profile firmware type matches radio")); + backupDir = g.currentProfile().pBackupDir(); + } + else { + // try to find a profile but there could be more than one profile for same variant + // if more than one take safer approach and use global + progress->addMessage(TR("Searching profiles for firmware type...")); + int cnt = 0; + int profId = 0; + QMap map; + map = g.getActiveProfiles(); + if (map.size() > 0) { + QMapIterator i(map); + while (i.hasNext()) { + i.next(); + if (g.getProfile(i.key()).getVariantFromType() == fw.getFlavour()) { + progress->addMessage(TR("Firmware type match found in profile %1 %2") + .arg(QString::number(i.key())) + .arg(i.value())); + profId = i.key(); + cnt++; + } + } + + progress->addMessage(TR("%1 matches found. Using profile %2 %3") + .arg(QString::number(cnt)) + .arg(QString::number(profId)) + .arg(g.getProfile(profId).name())); + + if (cnt > 0) + backupDir = g.getProfile(profId).pBackupDir(); + } + } + + if (backupDir.isEmpty()) + backupDir = g.backupDir(); + + progress->addMessage(TR("Backup directory: %1").arg(QDir::toNativeSeparators(backupDir))); + + // include time in file name as there could be multiple backups in a day + QString filePath = QString("%1/fw-%2-%3-%4.%5") + .arg(backupDir.replace("\\", "/")) // for Windows + .arg(fw.getFlavour()) + .arg(QDate(QDate::currentDate()).toString("yyyyMMdd")) + .arg(QTime(QTime::currentTime()).toString("HHmmss")) + .arg(fw.typeFileExtn()); + + if (promptForFile) { + filePath = QFileDialog::getSaveFileName(parent, TR("Save Firmware to File"), + filePath, getFirmwareFilesFilter()); + + if (filePath.isEmpty()) { + return false; + } + } else if (backupDir.isEmpty()) { + progress->addMessage(TR("Backup directory not configured for profile or application"), + QtFatalMsg); + return false; + } else if (!QFileInfo::exists(backupDir)) { + progress->addMessage(TR("Backup directory does not exist: %1") + .arg(QDir::toNativeSeparators(backupDir)), QtFatalMsg); + return false; + } + + progress->addMessage(TR("Writing firmware to: %1").arg(QDir::toNativeSeparators(filePath))); + + QFile f(filePath); + if (!f.open(QIODevice::ReadWrite)) { + progress->addMessage(TR("Error opening file for writing: %1 (reason: %2)") + .arg(QDir::toNativeSeparators(f.fileName())) + .arg(f.errorString()), QtFatalMsg); + return false; + } + + if (f.write(data) <= 0) { + progress->addMessage(TR("Error writing to file: %1 (reason: %2)") + .arg(QDir::toNativeSeparators(f.fileName())) + .arg(f.errorString()), QtFatalMsg); + f.close(); + return false; + } else { + msg = TR("Writing radio firmware to file finished"); + progress->addMessage(msg); + progress->setInfo(msg); + } + + f.close(); + return true; +} +QStringList dfuFileExtensions() +{ + return QStringList() << "bin" << "uf2"; +} + +QStringList uf2FileExtensions() +{ + return QStringList() << "uf2"; +} + +QStringList firmwareFileExtensions() +{ + return dfuFileExtensions(); +} diff --git a/companion/src/flash/radiointerface.h b/companion/src/flash/radiointerface.h new file mode 100644 index 000000000..553bc9b53 --- /dev/null +++ b/companion/src/flash/radiointerface.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include "rs_dfu.h" + +#include +#include + +using SliceU8 = rust::Slice; +using ::rust::Vec; + +class FirmwareReaderWorker : public QThread +{ + Q_OBJECT + + public: + explicit FirmwareReaderWorker(QObject *parent = nullptr); + ~FirmwareReaderWorker(); + + protected: + void run() override; + + signals: + void progressChanged(int value, int total); + void statusChanged(const QString &status); + void error(const QString &error); + void complete(const QByteArray& data); + + private: + void runDfu(); + void runUf2(); +}; + +class FirmwareWriterWorker : public QThread +{ + Q_OBJECT + + private: + QByteArray firmwareData; + + public: + explicit FirmwareWriterWorker(const QByteArray &data, QObject *parent = nullptr); + ~FirmwareWriterWorker(); + + protected: + void run() override; + + signals: + void progressChanged(int value, int total); + void statusChanged(const QString &status); + void error(const QString &error); + void complete(); + + private: + void writeUf2(DfuDevice &device, const SliceU8 &data); + void writeRegion(const DfuDevice &device, uint32_t addr, const SliceU8 &data); + void updateEraseStatus(size_t page, size_t pages); + void updateDownloadStatus(size_t bytes, size_t total); + void runDfu(); + void runUf2(); + + template + void rebootAndRediscover(DfuDevice &device, uint32_t addr, + const SliceU8 &data, uint32_t reboot_addr, + Duration timeout); +}; + + +class ProgressWidget; + +// TODO make a class +struct Uf2Info { + QString version; + QString board; + QString date; +}; + +QString findMassStoragePath(const QString &filename, bool onlyPath = false, ProgressWidget *progress = nullptr); + +bool readFirmware(const std::function& onComplete, + const std::function& onError, + ProgressWidget *progress); + +bool readFirmware(QByteArray &data, ProgressWidget *progress); +bool readFirmware(const QString &filename, ProgressWidget *progress); + +bool writeFirmware(const QByteArray &data, ProgressWidget *progress); +bool writeFirmware(const QString &filename, ProgressWidget *progress); + +bool readSettings(const QString &filename, ProgressWidget *progress); +bool readSettingsSDCard(const QString &filename, ProgressWidget *progress, bool fromRadio = true); + +bool writeSettings(const QString &filename, ProgressWidget *progress); + +QString printDevicesInfo(const Vec &devices); +QString getDevicesInfo(); +QString getFirmwareFilesFilter(); +Uf2Info getUf2Info(); +bool isDfuDeviceFound(); +bool isUf2DeviceFound(); +bool isRadioConnected(); +bool writeFirmwareToFile(QWidget *parent, const QByteArray &data, ProgressWidget *progress, bool promptForFile = false); +QStringList dfuFileExtensions(); +QStringList uf2FileExtensions(); +QStringList firmwareFileExtensions(); diff --git a/companion/src/splashlabel.h b/companion/src/flash/splashlabel.h similarity index 59% rename from companion/src/splashlabel.h rename to companion/src/flash/splashlabel.h index bdf287307..a6882ff87 100644 --- a/companion/src/splashlabel.h +++ b/companion/src/flash/splashlabel.h @@ -19,68 +19,42 @@ * GNU General Public License for more details. */ -#ifndef _SPLASHLABEL_H_ -#define _SPLASHLABEL_H_ +#pragma once #include -class splashLabel : public QLabel +class SplashLabel : public QLabel { Q_OBJECT public: - explicit splashLabel(QDialog * parent = 0) : - QLabel(parent) - { - } + explicit SplashLabel(QDialog * parent = 0) : QLabel(parent) {} - virtual void setId(int labelindex) - { - index = labelindex; - } - - virtual int getId() - { - return index; - } + virtual void setId(int labelindex) { index = labelindex; } + virtual int getId() { return index; } protected: virtual void mousePressEvent(QMouseEvent * event) { if (event->type() == QEvent::MouseButtonDblClick) { - emit buttonPressed(Qt::Key_Enter); + emit clicked(Qt::Key_Enter); } else { - emit buttonPressed(Qt::Key_Select); + emit clicked(Qt::Key_Select); } - // QWidget::mousePressEvent(event); } virtual void mouseReleaseEvent(QMouseEvent * event) { setFocus(); - // emit buttonPressed(0); - // QWidget::mouseReleaseEvent(event); } - - /*void paintEvent(QPaintEvent *) - { - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); - } - */ signals: - void buttonPressed(int button); + void clicked(int button); private: int index; }; - -#endif // _SPLASHLABEL_H_ - diff --git a/companion/src/splashlibrarydialog.cpp b/companion/src/flash/splashlibrarydialog.cpp similarity index 81% rename from companion/src/splashlibrarydialog.cpp rename to companion/src/flash/splashlibrarydialog.cpp index 0b843e31a..42edecbbb 100644 --- a/companion/src/splashlibrarydialog.cpp +++ b/companion/src/flash/splashlibrarydialog.cpp @@ -37,14 +37,18 @@ SplashLibraryDialog::SplashLibraryDialog(QWidget *parent, QString * filename): ui->prevPage->setIcon(CompanionIcon("arrow-left.png")); page = 0; getFileList(); - if (imageList.size() > 20) { + + if (imageList.size() > 20) ui->nextPage->setEnabled(true); - } + setupPage(page); - foreach(splashLabel *sl, findChildren()) { - connect(sl, SIGNAL(buttonPressed(int)), this, SLOT(onButtonPressed(int))); + foreach(SplashLabel *sl, findChildren()) { + connect(sl, &SplashLabel::clicked, this, &SplashLibraryDialog::imageClicked); } + + connect(ui->nextPage, &QPushButton::clicked, this, &SplashLibraryDialog::nextPageClicked); + connect(ui->prevPage, &QPushButton::clicked, this, &SplashLibraryDialog::prevPageClicked); } SplashLibraryDialog::~SplashLibraryDialog() @@ -54,12 +58,13 @@ SplashLibraryDialog::~SplashLibraryDialog() void SplashLibraryDialog::setupPage(int page) { - splashLabel * sl[] = { ui->FwImage_01, ui->FwImage_02, ui->FwImage_03, ui->FwImage_04 , + SplashLabel *sl[] = { ui->FwImage_01, ui->FwImage_02, ui->FwImage_03, ui->FwImage_04 , ui->FwImage_05, ui->FwImage_06, ui->FwImage_07, ui->FwImage_08 , ui->FwImage_09, ui->FwImage_10, ui->FwImage_11, ui->FwImage_12 , ui->FwImage_13, ui->FwImage_14, ui->FwImage_15, ui->FwImage_16 , ui->FwImage_17, ui->FwImage_18, ui->FwImage_19, ui->FwImage_20 }; - for(int i=0; i<20; i++) { + + for(int i = 0; i < 20; i++) { if ((i + 20 * page) < imageList.size()) { QImage image(imageList.at(i + 20 * page)); if (!image.isNull()) { @@ -67,15 +72,13 @@ void SplashLibraryDialog::setupPage(int page) sl[i]->setEnabled(true); sl[i]->setId((i + 20 * page)); sl[i]->setStyleSheet("border:1px solid; border-color:#999999;"); - } - else { + } else { sl[i]->clear(); sl[i]->setDisabled(true); sl[i]->setStyleSheet("border:1px;"); sl[i]->setId(-1); } - } - else { + } else { sl[i]->clear(); sl[i]->setDisabled(true); sl[i]->setStyleSheet("border:1px;"); @@ -89,14 +92,18 @@ void SplashLibraryDialog::getFileList() { imageList.clear(); if (g.embedSplashes()) { - QDir myRes(":/images/library"); + const QString imgLocn(":/images/library"); + QDir myRes(imgLocn); QStringList tmp = myRes.entryList(); + for (int i = 0; i < tmp.size(); i++) { QFileInfo fileInfo(tmp.at(i)); - imageList.append(":/images/library/" + fileInfo.fileName()); + imageList.append(imgLocn + "/" + fileInfo.fileName()); } } + QString libraryPath = g.libDir(); + if (!libraryPath.isEmpty()) { if (QDir(libraryPath).exists()) { QStringList supportedImageFormats; @@ -112,59 +119,60 @@ void SplashLibraryDialog::getFileList() QImage image(filename); if (!image.isNull()) { imageList.append(filename); - } - else { + } else { QMessageBox::warning(this, CPN_STR_TTL_WARNING, tr("Invalid image in library %1").arg(filename)); } } } } + if (imageList.size() == 0) { QMessageBox::information(this, CPN_STR_TTL_INFO, tr("No valid image found in library, check your settings")); - QTimer::singleShot(0, this, SLOT(dclose())); + QTimer::singleShot(0, [=]() { close(); }); } else { imageList.sort(); } } -void SplashLibraryDialog::dclose() +void SplashLibraryDialog::imageClicked(int button) { - close(); -} + SplashLabel * myLabel = qobject_cast(sender()); -void SplashLibraryDialog::onButtonPressed(int button) -{ - splashLabel * myLabel = qobject_cast(sender()); - - foreach(splashLabel *sl, findChildren()) { + foreach(SplashLabel *sl, findChildren()) { if (sl->isEnabled()) { sl->setStyleSheet("border:1px solid; border-color:#999999;"); } } + if (button == Qt::Key_Enter) { int id = myLabel->getId(); splashFileName->clear(); splashFileName->append(imageList.at(id)); close(); } + myLabel->setStyleSheet("border:1px solid; border-color:#00ffff"); } -void SplashLibraryDialog::on_nextPage_clicked() { +void SplashLibraryDialog::nextPageClicked() { page++; + if (page >= (imageList.size() / 20)) { ui->nextPage->setDisabled(true); } + ui->prevPage->setEnabled(true); setupPage(page); } -void SplashLibraryDialog::on_prevPage_clicked() { +void SplashLibraryDialog::prevPageClicked() { page--; + if (page == 0) { ui->prevPage->setDisabled(true); } + if (imageList.size() > 20) { ui->nextPage->setEnabled(true); } diff --git a/companion/src/splashlibrarydialog.h b/companion/src/flash/splashlibrarydialog.h similarity index 70% rename from companion/src/splashlibrarydialog.h rename to companion/src/flash/splashlibrarydialog.h index ee077f1f4..d428baf9d 100644 --- a/companion/src/splashlibrarydialog.h +++ b/companion/src/flash/splashlibrarydialog.h @@ -19,37 +19,34 @@ * GNU General Public License for more details. */ -#ifndef _SPLASHLIBRARYDIALOG_H_ -#define _SPLASHLIBRARYDIALOG_H_ +#pragma once -#include +#include namespace Ui { - class SplashLibraryDialog; + class SplashLibraryDialog; } class SplashLibraryDialog : public QDialog { Q_OBJECT -public: - explicit SplashLibraryDialog(QWidget *parent = 0, QString * fileName=NULL); + public: + explicit SplashLibraryDialog(QWidget *parent = 0, QString *fileName = nullptr); ~SplashLibraryDialog(); -private slots: - void dclose(); - void onButtonPressed(int button); - void on_nextPage_clicked(); - void on_prevPage_clicked(); - -private: - void getFileList(); - void setupPage(int page); + private slots: + void imageClicked(int button); + void nextPageClicked(); + void prevPageClicked(); + + private: Ui::SplashLibraryDialog *ui; - QString * splashFileName; + QString *splashFileName; QString libraryPath; QStringList imageList; int page; -}; -#endif // _SPLASHLIBRARYDIALOG_H_ + void getFileList(); + void setupPage(int page); +}; diff --git a/companion/src/splashlibrarydialog.ui b/companion/src/flash/splashlibrarydialog.ui similarity index 88% rename from companion/src/splashlibrarydialog.ui rename to companion/src/flash/splashlibrarydialog.ui index 3ecda7d51..63e4d4bbb 100644 --- a/companion/src/splashlibrarydialog.ui +++ b/companion/src/flash/splashlibrarydialog.ui @@ -57,10 +57,10 @@ - QLayout::SetNoConstraint + QLayout::SizeConstraint::SetNoConstraint - + 0 @@ -86,10 +86,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Raised + QFrame::Shadow::Raised 0 @@ -106,7 +106,7 @@ - + 0 @@ -132,10 +132,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -149,7 +149,7 @@ - + 0 @@ -175,10 +175,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -192,7 +192,7 @@ - + 0 @@ -218,10 +218,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -235,7 +235,7 @@ - + 0 @@ -255,10 +255,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -272,7 +272,7 @@ - + 0 @@ -292,10 +292,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -309,7 +309,7 @@ - + 0 @@ -329,10 +329,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -346,7 +346,7 @@ - + 0 @@ -372,10 +372,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -389,7 +389,7 @@ - + 0 @@ -409,10 +409,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -426,7 +426,7 @@ - + 0 @@ -446,10 +446,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -463,7 +463,7 @@ - + 0 @@ -483,10 +483,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -500,7 +500,7 @@ - + 0 @@ -526,10 +526,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -543,7 +543,7 @@ - + 0 @@ -563,10 +563,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -580,7 +580,7 @@ - + 0 @@ -600,10 +600,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -617,7 +617,7 @@ - + 0 @@ -637,10 +637,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -654,7 +654,7 @@ - + 0 @@ -680,10 +680,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -697,7 +697,7 @@ - + 0 @@ -717,10 +717,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -734,7 +734,7 @@ - + 0 @@ -754,10 +754,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -771,7 +771,7 @@ - + 0 @@ -791,10 +791,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -808,7 +808,7 @@ - + 0 @@ -834,10 +834,10 @@ border:1px; - QFrame::Panel + QFrame::Shape::Panel - QFrame::Plain + QFrame::Shadow::Plain 0 @@ -889,7 +889,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -902,7 +902,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -916,13 +916,13 @@ - splashLabel + SplashLabel QLabel
splashlabel.h
- + diff --git a/companion/src/flashfirmwaredialog.cpp b/companion/src/flashfirmwaredialog.cpp deleted file mode 100644 index b06cadb1c..000000000 --- a/companion/src/flashfirmwaredialog.cpp +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "flashfirmwaredialog.h" -#include "ui_flashfirmwaredialog.h" -#include "appdata.h" -#include "helpers.h" -#include "progressdialog.h" -#include "radiointerface.h" -#include "progresswidget.h" -#include "splashlibrarydialog.h" -#include "storage.h" - -#if defined _MSC_VER || !defined __GNUC__ - #include - #define sleep(x) Sleep(x*1000) -#else - #include -#endif - -FlashFirmwareDialog::FlashFirmwareDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::FlashFirmwareDialog), - fwName(g.profile[g.id()].fwName()) -{ - ui->setupUi(this); - - if (!g.profile[g.id()].splashFile().isEmpty()){ - imageSource = PROFILE; - imageFile = g.profile[g.id()].splashFile(); - ui->useProfileSplash->setChecked(true); - } - else { - imageSource = FIRMWARE; - imageFile = ""; - ui->useProfileSplash->setDisabled(true); - } - - if (IS_STM32(getCurrentBoard())) { - // No backup on Taranis ... could be done if in massstorage - ui->backupEEprom->hide(); - ui->backupEEprom->setCheckState(Qt::Unchecked); - } - else { - ui->backupEEprom->setCheckState(g.backupOnFlash() ? Qt::Checked : Qt::Unchecked); - } - - QString backupPath = g.profile[g.id()].pBackupDir(); - if (backupPath.isEmpty()) { - backupPath=g.backupDir(); - } - if (backupPath.isEmpty() || !QDir(backupPath).exists()) { - ui->backupEEprom->setEnabled(false); - } - - ui->checkHardwareCompatibility->setChecked(g.checkHardwareCompatibility()); - - updateUI(); - - resize(0, 0); // TODO needed? -} - -FlashFirmwareDialog::~FlashFirmwareDialog() -{ - delete ui; -} - -void FlashFirmwareDialog::updateUI() -{ - ui->firmwareFilename->setText(fwName); - ui->burnButton->setEnabled(QFile(fwName).exists()); - - FirmwareInterface firmware(fwName); - if (firmware.isValid()) { - ui->firmwareInfoFrame->show(); - ui->date->setText(firmware.getDate() + " " + firmware.getTime()); - ui->version->setText(firmware.getVersion()); - ui->variant->setText(firmware.getEEpromId()); - ui->date->setEnabled(true); - ui->version->setEnabled(true); - ui->variant->setEnabled(true); - if (firmware.hasSplash()) { - ui->splashFrame->show(); - ui->splash->setFixedSize(firmware.getSplashWidth(), firmware.getSplashHeight()); - } - else { - ui->splashFrame->hide(); - } - } - else { - imageSource = FIRMWARE; - ui->firmwareInfoFrame->hide(); - ui->splashFrame->hide(); - } - - QImage image; - switch (imageSource) { - case FIRMWARE: - ui->useFirmwareSplash->setChecked(true); - image = firmware.getSplash(); - break; - case PROFILE: - ui->useProfileSplash->setChecked(true); - image.load(g.profile[g.id()].splashFile()); - break; - case LIBRARY: - ui->useLibrarySplash->setChecked(true); - image.load(imageFile); - break; - case EXTERNAL: - ui->useExternalSplash->setChecked(true); - image.load(imageFile); - break; - } - - if (!image.isNull()) { - ui->splash->setPixmap(makePixMap(image)); - } -} - -void FlashFirmwareDialog::on_firmwareLoad_clicked() -{ - QString fileName = QFileDialog::getOpenFileName(this, tr("Open Firmware File"), g.flashDir(), FLASH_FILES_FILTER); - if (!fileName.isEmpty()) { - fwName = fileName; - if (!fwName.isEmpty() && !fwName.endsWith(".dfu") && !FirmwareInterface(fwName).isValid()) { - QMessageBox::warning(this, CPN_STR_TTL_WARNING, tr("%1 may not be a valid firmware file").arg(fwName)); - } - updateUI(); - } -} - -void FlashFirmwareDialog::on_useFirmwareSplash_clicked() -{ - FirmwareInterface firmware(fwName); - if (!firmware.isValid()) { - QMessageBox::warning(this, CPN_STR_TTL_ERROR, tr( "The firmware file is not valid." )); - } - else if (!firmware.hasSplash()) { - QMessageBox::warning(this, CPN_STR_TTL_ERROR, tr( "There is no start screen image in the firmware file." )); - } - else { - imageSource = FIRMWARE; - } - updateUI(); -} - -void FlashFirmwareDialog::on_useProfileSplash_clicked() -{ - QString fileName = g.profile[g.id()].splashFile(); - if (!fileName.isEmpty()) { - QImage image(fileName); - if (image.isNull()) { - QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Profile image %1 is invalid.").arg(fileName)); - } - else { - imageSource = PROFILE; - } - } - updateUI(); -} - -void FlashFirmwareDialog::on_useExternalSplash_clicked() -{ - QString supportedImageFormats; - for (int formatIndex = 0; formatIndex < QImageReader::supportedImageFormats().count(); formatIndex++) { - supportedImageFormats += QLatin1String(" *.") + QImageReader::supportedImageFormats()[formatIndex]; - } - QString fileName = QFileDialog::getOpenFileName(this, tr("Open image file to use as radio start screen"), g.imagesDir(), tr("Images (%1)").arg(supportedImageFormats)); - if (!fileName.isEmpty()){ - g.imagesDir( QFileInfo(fileName).dir().absolutePath() ); - QImage image(fileName); - if (image.isNull()) { - QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Image could not be loaded from %1").arg(fileName)); - } - else{ - imageSource = EXTERNAL; - imageFile = fileName; - } - } - updateUI(); -} - -void FlashFirmwareDialog::on_useLibrarySplash_clicked() -{ - QString fileName; - auto ld = new SplashLibraryDialog(this, &fileName); - ld->exec(); - if (!fileName.isEmpty()) { - QImage image(fileName); - if (image.isNull()) { - QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("The library image could not be loaded")); - } - else { - imageSource = LIBRARY; - imageFile = fileName; - } - } - updateUI(); -} - -void FlashFirmwareDialog::on_burnButton_clicked() -{ - g.flashDir(QFileInfo(fwName).dir().absolutePath()); - g.profile[g.id()].fwName(fwName); - g.checkHardwareCompatibility(ui->checkHardwareCompatibility->isChecked()); - g.backupOnFlash(ui->backupEEprom->isChecked()); - - qDebug() << "FlashFirmwareDialog: flashing" << fwName; - - if (imageSource != FIRMWARE) { - // load the splash image - const QPixmap pixmap = ui->splash->pixmap(Qt::ReturnByValue); - QImage image; - image = pixmap.toImage().scaled(ui->splash->width(), ui->splash->height()); - if (image.isNull()) { - QMessageBox::critical(this, CPN_STR_TTL_WARNING, tr("Splash image not found")); - return; - } - // write the customized firmware - QString tempFile; - if (getStorageType(fwName) == STORAGE_TYPE_HEX) - tempFile = generateProcessUniqueTempFileName("flash.hex"); - else - tempFile = generateProcessUniqueTempFileName("flash.bin"); - qDebug() << "FlashFirmwareDialog: patching" << fwName << "with custom splash screen and saving to" << tempFile; - FirmwareInterface firmware(fwName); - firmware.setSplash(image); - if (firmware.save(tempFile) <= 0) { - QMessageBox::critical(this, CPN_STR_TTL_WARNING, tr("Cannot save customized firmware")); - return; - } - startFlash(tempFile); - } - else { - startFlash(fwName); - } -} - -void FlashFirmwareDialog::on_cancelButton_clicked() -{ - close(); -} - -void FlashFirmwareDialog::shrink() -{ - resize(0, 0); -} - -void FlashFirmwareDialog::startFlash(const QString &filename) -{ -// bool backup = g.backupOnFlash(); - - close(); - - ProgressDialog progressDialog(this, tr("Write Firmware to Radio"), CompanionIcon("write_flash.png")); - - // check hardware compatibility if requested - bool checkPassed = true; - if (g.checkHardwareCompatibility()) { - QString tempFirmware = generateProcessUniqueTempFileName("flash-check.bin"); - if (!readFirmware(tempFirmware, progressDialog.progress())) { - QMessageBox::warning(this, tr("Firmware check failed"), tr("Could not check firmware from radio")); - checkPassed = false; - } - FirmwareInterface previousFirmware(tempFirmware); - qunlink(tempFirmware); - FirmwareInterface newFirmware(filename); - qDebug() << "startFlash: checking firmware compatibility between " << tempFirmware << "and" << filename; - if (!newFirmware.isHardwareCompatible(previousFirmware)) { - QMessageBox::warning(this, tr("Firmware check failed"), tr("New firmware is not compatible with the one currently installed!")); - if (isTempFileName(filename)) { - qDebug() << "startFlash: removing temporary file" << filename; - qunlink(filename); - } - checkPassed = false; - } - } - - if (checkPassed) { - // backup if requested -// bool result = true; -// QString backupFilename; -// QString backupPath; -// if (backup) { -// backupPath = g.profile[g.id()].pBackupDir(); -// if (backupPath.isEmpty()) { -// backupPath=g.backupDir(); -// } -// backupFilename = backupPath + "/backup-" + QDateTime().currentDateTime().toString("yyyy-MM-dd-HHmmss") + ".bin"; -// result = readSettings(backupFilename, progressDialog.progress()); -// sleep(2); -// } - - // flash - //result = (result && writeFirmware(filename, progressDialog.progress())); - writeFirmware(filename, progressDialog.progress()); - - // restore if backup requested -// if (backup && result) { -// sleep(2); -// QString restoreFilename = generateProcessUniqueTempFileName("restore.bin"); -// if (!convertEEprom(backupFilename, restoreFilename, filename)) { -// QMessageBox::warning(this, tr("Conversion failed"), tr("Cannot convert Models and Settings for use with this firmware, original data will be used")); -// restoreFilename = backupFilename; -// } -// if (!writeSettings(restoreFilename, progressDialog.progress())) { -// QMessageBox::warning(this, tr("Restore failed"), tr("Could not restore Models and Settings to Radio. The models and settings data file can be found at: %1").arg(backupFilename)); -// } -// } - - progressDialog.progress()->setInfo(tr("Flashing done")); - } - - progressDialog.exec(); - - if (isTempFileName(filename)) { - qDebug() << "startFlash: removing temporary file" << filename; - qunlink(filename); - } -} diff --git a/companion/src/fusesdialog.cpp b/companion/src/fusesdialog.cpp deleted file mode 100644 index 6a83dbc6f..000000000 --- a/companion/src/fusesdialog.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "fusesdialog.h" -#include "ui_fusesdialog.h" -#include "helpers.h" -#include "progressdialog.h" -#include "radiointerface.h" - -FusesDialog::FusesDialog(QWidget *parent) : -QDialog(parent), -ui(new Ui::FusesDialog) -{ - ui->setupUi(this); - setWindowIcon(CompanionIcon("fuses.png")); -} - -FusesDialog::~FusesDialog() -{ - delete ui; -} - -void FusesDialog::on_resetFuses_EEprotect_clicked() -{ - ProgressDialog progressDialog(this, tr("Reset Radio Fuses"), CompanionIcon("fuses.png"), true); - return resetAvrdudeFuses(true, progressDialog.progress()); -} - -void FusesDialog::on_resetFuses_EEdelete_clicked() -{ - ProgressDialog progressDialog(this, tr("Reset Radio Fuses"), CompanionIcon("fuses.png"), true); - return resetAvrdudeFuses(false, progressDialog.progress()); -} - -void FusesDialog::on_readFuses_clicked() -{ - ProgressDialog progressDialog(this, tr("Read Fuses from Radio"), CompanionIcon("fuses.png"), true); - return readAvrdudeFuses(progressDialog.progress()); -} diff --git a/companion/src/fusesdialog.h b/companion/src/fusesdialog.h deleted file mode 100644 index 08da67bb6..000000000 --- a/companion/src/fusesdialog.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _FUSESDIALOG_H_ -#define _FUSESDIALOG_H_ - -#include - -namespace Ui { - class FusesDialog; -} - -class FusesDialog : public QDialog -{ - Q_OBJECT - -public: - explicit FusesDialog(QWidget *parent = 0); - ~FusesDialog(); - -private slots: - void on_readFuses_clicked(); - void on_resetFuses_EEdelete_clicked(); - void on_resetFuses_EEprotect_clicked(); - -private: - Ui::FusesDialog *ui; - -}; - -#endif // _FUSESDIALOG_H_ diff --git a/companion/src/fusesdialog.ui b/companion/src/fusesdialog.ui deleted file mode 100644 index e9e161062..000000000 --- a/companion/src/fusesdialog.ui +++ /dev/null @@ -1,118 +0,0 @@ - - - FusesDialog - - - - 0 - 0 - 374 - 207 - - - - Fuses - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Reads the current fuses in the AVR controller.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Proper states for </span><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">AtMega 64 </span><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">EEPROM erase fuse not set: 0E, 81, FF</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">EEPROM erase fuse set: 0E, 89, FF</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Proper states for AtMega 2560 :</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">EEPROM erase fuse not set: D7, 11, FC</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">EEPROM erase fuse set: D7, 19, FC</span></p></body></html> - - - Read Fuses - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:600; text-decoration: underline;">Reset Fuses</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Fuses in the AVR tell it how to behave. Pressing this button sets the fuses to the default parameters needed in the FW. These parameters are different for stock and 4.1 MB, please verify you selected the appropriate processor type in preferences.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">This button also sets the &quot;EEPROM protect&quot; Fuse.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">This prevents erasing of the EEPROM when the flash memory is written.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:600; text-decoration: underline;">WARNING</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Setting fuses can lead to problems and even a total lockout from your controller. Do this only if you know what you are doing.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">When in doubt consult either the project's page or the 9xforum (http://9xforums.com/forum/)</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">If you do get locked out - google lookup for &quot;dealing with Fuse Bricks&quot;.</span></p></body></html> - - - Reset Fuses -EEPROM - PROTECT - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:600; text-decoration: underline;">Reset Fuses</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Fuses in the AVR tell it how to behave. Pressing this button sets the fuses to the default parameters needed in the </span><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">FW. These parameters are different for stock and 4.1 MB, please verify you selected the appropriate processor type in preferences.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">This button also clears the &quot;EEPROM protect&quot; Fuse.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">This causes erasing of the EEPROM when the flash memory is written.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:600; text-decoration: underline;">WARNING</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Setting fuses can lead to problems and even a total lockout from your controller. Do this only if you know what you are doing.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">When in doubt consult either the project's page or the 9xforum (http://9xforums.com/forum/)</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">If you do get locked out - google lookup for &quot;dealing with Fuse Bricks&quot;.</span></p></body></html> - - - Reset Fuses -EEPROM - DELETE - - - - - - - QFrame::Panel - - - QFrame::Raised - - - 2 - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> -<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; text-decoration: underline;">WARNING</span></p> -<p align="center" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600; text-decoration: underline;"></p> -<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Changing the fuses can mess up your radio.</p> -<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Proceed only if you know what you are doing.</p></body></html> - - - - - - - - - - diff --git a/companion/src/helpers.cpp b/companion/src/helpers.cpp index 9417f4b37..14c50dace 100644 --- a/companion/src/helpers.cpp +++ b/companion/src/helpers.cpp @@ -232,23 +232,6 @@ static bool caseInsensitiveLessThan(const QString &s1, const QString &s2) return s1.toLower() < s2.toLower(); } -bool displayT16ImportWarning() -{ - QMessageBox msgBox; - msgBox.setWindowTitle(QObject::tr("WARNING")); - msgBox.setText(QObject::tr("

Importing JumperTX data into OpenTX 2.3 is not supported and dangerous.

\ -

It is unfortunately not possible for us to differentiate JumperTX data from legitimate FrSky X10 data, but You should only continue here if the file you opened comes from a real FrSky X10.

\ -

Do you really want to continue?

")); - msgBox.setIcon(QMessageBox::Warning); - msgBox.addButton(QMessageBox::No); - msgBox.addButton(QMessageBox::Yes); - msgBox.setDefaultButton(QMessageBox::No); - - if (msgBox.exec() == QMessageBox::No) - return false; - return true; -} - void Helpers::populateFileComboBox(QComboBox * b, const QSet & set, const QString & current) { b->clear(); @@ -347,6 +330,26 @@ int Helpers::getFirstPosValueIndex(QComboBox * cbo) return -1; } +QString Helpers::concatPath(QString & str1, QString & str2, bool onlyonesep) +{ + return (str1 % ((!str1.endsWith("/") || !onlyonesep) ? "/" : "") % str2); +} + +QString Helpers::concatPath(const QString & str1, const QString & str2, bool onlyonesep) +{ + return (str1 % ((!str1.endsWith("/") || !onlyonesep) ? "/" : "") % str2); +} + +QString Helpers::concatPath(const QString & str1, QString & str2, bool onlyonesep) +{ + return (str1 % ((!str1.endsWith("/") || !onlyonesep) ? "/" : "") % str2); +} + +QString Helpers::concatPath(QString & str1, const QString & str2, bool onlyonesep) +{ + return (str1 % ((!str1.endsWith("/") || !onlyonesep) ? "/" : "") % str2); +} + #ifdef __APPLE__ // Flag when simulator is running static bool simulatorRunning = false; diff --git a/companion/src/helpers.h b/companion/src/helpers.h index 473504989..05952d920 100644 --- a/companion/src/helpers.h +++ b/companion/src/helpers.h @@ -56,8 +56,6 @@ extern const QColor colors[CPN_MAX_CURVES]; #define TRIM_MODE_NONE 0x1F // 0b11111 #define TRIM_MODE_3POS (2 * CPN_MAX_FLIGHT_MODES) -bool displayT16ImportWarning(); - class CompanionIcon: public QIcon { public: CompanionIcon(const QString &baseimage); @@ -119,6 +117,10 @@ namespace Helpers unsigned int getBitmappedValue(const unsigned int & field, const unsigned int index = 0, const unsigned int numbits = 1, const unsigned int offset = 0); void setBitmappedValue(unsigned int & field, unsigned int value, unsigned int index = 0, unsigned int numbits = 1, unsigned int offset = 0); int getFirstPosValueIndex(QComboBox * cbo); + QString concatPath(QString & str1, QString & str2, bool onlyonesep = true); + QString concatPath(const QString & str1, const QString & str2, bool onlyonesep = true); + QString concatPath(const QString & str1, QString & str2, bool onlyonesep = true); + QString concatPath(QString & str1, const QString & str2, bool onlyonesep = true); } // namespace Helpers diff --git a/companion/src/images/library/00001.png b/companion/src/images/library/00001.png deleted file mode 100644 index 43cd387dc7b790ab437796d6464fd402a305adfa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2331 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 diff --git a/companion/src/images/library/00002.png b/companion/src/images/library/00002.png deleted file mode 100644 index 37b24b0d7906cc76c1ae91f3baa9ba323596dcbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4365 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 diff --git a/companion/src/images/library/00003.png b/companion/src/images/library/00003.png deleted file mode 100644 index 6e45e310a4ff420fc799ee8312964ab385a429cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1358 zcmV-U1+n^xP)sXYiTwb30aj&j0-nK zTqIR!Y;s)q-u2G8bMKwG_a^V%H*>PN=gj%uJCpO~%>C}nTtNf~0EC)&tsylcQN5&~ zS+9uzl;-Ji#pC4Lyw!k78jL+Xu6SGpDI$OTknM^rw z6_=R|X{Yh|((L+Iahb_vO?44MmLB4r?k1C|XQFK4l%C1ddsLalDM0QrnMNvoY5kL~ zGnx8|d7QFVsfE%gl;50AYYrR$41p~Eh5!Ivn2xmUVM%xo&muloG$Y!qf6`?plU*L- z$B6BFv?%(1e8@Z=<7lMVnf6cSDF)4sOOhe{$t<7Xn~=5y$65OfdhjxHoU)cX$}m+o zJ1$9P(2akYZ2*gc>jPN9T2Es*N1T$Xnbc*If-IAga|*SU8JADSagbR)Mql-6Df(I4 zD1L|?Lni%`o;hWg>$uA-C(sH2ID`(x{vWzAopPg|+&{J+)$Gj4gh!DD+xh@wSgBZ! z;WU=v3ECCwcQ_OM*IV^Znllp0$YdHDmrtH!FSGQc75ywt_i-*}Mlz36nlln)WHOD6 z%P+s-G8<#P_*St@;cLt$jA*m|1&@$~=JBQrTE5Z;0Ie9o`j9?}rAqrgT-DDyR&h$t zobt*me1-o*x|*h6wP~`C(=t&mGpBfYpm|PFo5Okwij$Q-S9j0lDSZI20Qaz2W8aBi z5^lQ% zhazbLE~efBSoF{3^HIf(sEruLLXG`6PQ{KWyZ(6|RY0QN#|YMGTV7)m9wj8IKpy3p zQ@UJI()22H-GbX#T*!B%CdxCXH2D%eiaw|;4{pHOV!nog1$`n4E$>rA5Br4M%8S!) zi{O&HiDC^+D&AAnK^NF3?9hITUH=?EAC*^Ha0hLQ5a-WF!Jm(^zhOw}pV_;RnscqHk7Q!ZhQF$5?;0rCrRfhh=3fC2&)pa9*FB;3p|m!7^4q9{TXCH(nkE=r0^(7r<~ z2mxf7qDzLRi$OrZ2m!!AK=g}@fPv}4rB4G00T^S7bb$ec zgT*4?&O2M6Q$Vl+bgK%~FTbqAxpU{iaTVb$L{(K4-vI!WF;Gg;y?Y|sv}qsUm;l{+ zKGc%$m!PD$1U$!q0w7>uOfm6@YVr|qAcO!M0q3c*bn8(4x6>)&_SUWe@L97v)F-XbV&lqyt=Fc7Aog3rnm0w=x{+wvvW;0| z9ZAQbLq~A@_$keZ`oIO8ZRLgkrN#@Dx6 ztq2LRp=ZyoXw#;H>Aq8^GV$}z$&f@5gyUe06D$AEaOWK{c;JDD0$ddksGEy2GY?|L zk3Yd;v8dR$8s239BZ`h9P=dIW033*hLcs-S0bnTUO$-c)i3zy>{$YVh4G7fDL(7*h^+=Qz1Octv z+=P&!3XW4vKuQs8v*Eetp4Mun4j$Z#70Z8uEXz8uq3Wy9urN%Y zK0Ux?0fD+X=z|aDAm`6Kz)S9{BuNMl4@3R>^^uU!0X=&3@wn%3#sREbyAcHi1)5M& znZg!g!y9kBq20G;%_?l)wiCPordRwbf_b=T)ejfH8{1!~~2OF;ctd*=I*1EG$&hPGAg9r-!Ss|^6CSFZtho+2Y7QxO!#aOu)zaD+o# zTs#1vPMuJsr0j(#idv>R1H;9Omk=9!2kO?nJ-~4RAL{0(_U$|2g%>8EOP5ZNBnb=* zSBgupWXW>VeS-$whXxJnLy{%Pk_3+Du;=#_?RtX-&C$F?Q;4DnS=Rk2i-kvW^5(!q z2L$TYqNko3g}4swAv#4+N^$VuVSM-PV$(eX2lNF2QCg5uijv||{J3J7c0H~`I|zaR zS*Dsm0R;Q^9|%lvK%j0t8vDXHbWDgtSy>qfN3iPWwWjOcy4{5!OOU3e1cAp7KdjWQ zH*3}eHe0YFOa|0XmSvngkr9~So1CtybL_%}U!tJkf(MFLeP{&cCPZpp95Z}qx7#se z#v9jVQpSy&fb8s-ke8Q_V5=4HzB?OJrcBkY_qn?lR{r=i1VI1@AP7A6?%jhr0a*-%(m#vXR;Afg{0a1{48W==itnlY-D9+S80A-`Gk7uzHweX+ow^+ z5M8exMvi>i=QH1Yb0%yyo7?&@x8Eh|C+hz)AThBM`t}`A;dqxVTY{XNTxG1T{)w(0 z9!!Z71_xVl#~m@a@4g2;zCUy341|S+R(ow3W3X5(m@whRYJD#w;{bm7Wuw=%kYx!G z5fMmA8c=hAI(IG`nVCnCbvhg1T-}89Nq6grSAB`n>tZ2EGGb#}UuW}g1ZL0v0F+Yf z-Mb&BP9H^!=B)q#!ykGC-+r@HTTlidHFZBaby8Y8$H#ZTnKRiYfnto|_uo^|zkgDN zzx&NM3z3#~2)TLrV3dO6I4}lW?VjaMlv2dUcc}25l$5;)3lG(s+W^Wym?o!Y0p@`C zxDJ&HQ`YID*tTs4E?+73$|JHYqhmrGdiLyw#KfLnpJ6X8#gQYKTELyClBvuUGfJte zf(Hyr=ishgJ5_tWHf-38{rl5A=TWAp-yj-;2j7Ec&05vm|%pkiY?A}+2SoT3vP;jrMVMW*X9G0os` zI3dY0K!B8#KeTK0qN5C^p5jYUN|BS3Tj6hx9z7cC*KbB{ZXO8W#w-lJ~r1odNQMN*pOwZ(v8HOcJADYw6sH}=MpG$7!nqOmtUTU zcI^_b&g3g7xPY_yXJHGm)j%Wp)=l0>6rJeT@1E;!4>xJj7<>Mc0*Ohm+sjSY+O>>joR~=OFMrD5z<;ZQHcQlTSW| zX3bhv`ka<6TR;?@YG7We9Mw!-wF|JzJY!5PC?y0=rxUGPSG}oD#^L?={PTryI-MqS zk5Y=n#7-XZ${Gt)N=hog5f#O%WN8T?in8ACj<_$j(!8I6cCz<>78M~a?GMDoDNGm~91Kwu6+a4qBL_xt}37H%~=DWe@-orLbCpFl^XRJo@N}s-tNf2ae+* zQ%TM4cjFzS)#qjgrljn}uwj)qD@{*N#rpM|aOFy|DOgV#L%+N4!q~Cn{R2vzJb470 zHf;r0C4o|Qg&sY+f>OB}yQ`A6vw8DYJoM20{}q8MFDplRdAVY;$<(w%_3YUVOP4N( z!{Gp94E5_bHGOyZ@cXfN@e+iEg@WTal$MlY{P^cReq-s9XlGau^p@RE_LhHMS}*>$j!|INAwm3 zF1NurPNP6k#&GuRxk{hEd2=$p{Bki$?RErNg0!_^j3Mcs0eEiASpT7XUwyS0qA0>* zsr>7*OwpuCL;UlfGit=tFTR+AsHjH&6@hBsp%dcc+hfa?Z3qnwMdQYe4d!4=yz|aX zy#4lj2oAR5siz)y12z5!(b4s=cI|pxxKN03;#v1rkEum%NHNz<2>mSWbd zndsK->io$aJGLM{|7?X0IN>j6y?`vs=+UDq+P7Dbpea+PAtt6dx^_)O zojUbAfQ#?H|K8`=lahuaDam)~#wVZ7g-m70GUpqJR>7I_atB6?c=W1;2>|Z8tG~}{ zyLN3wQBe_O4-H66Cc0~WWm!hMc5OVKbL`k5{QdcfuvmjMfXVevk&$&Ue*E(oIB;-{ z-yu4>DeB!Gg%ih5DrU8Qc99pqT&)sSKir$Tzd``wi!T;x>0`uoiVGp=-n}a(c}bkU zDuJ3a=YL_b2%wbu+yPvwmQsr7`t{Jca}U31jl#krEL`}F39=?W!3+^29{;OS1h`@) zwr>3mj8Tu~Dte4U@iFeZ?;bbv?XTBF1Af2c2ZV)%xy>arn=f#+I%60$>bYyKmDjFK zMsaZoc%D}o$`{Z(@3`_6r004y- z3Nd-|G?dxxummZ)rEVO+WGdM0c8nZ30*xBQTyw^qKK&I~tyY6CWIWA4kMA%-KxG+C znl#4l-P@EvtFMj3wG50QG&B^gT2=4(oxOYa;J|@&Fv=h?$v48Jz-F)r7PN04tMkX% zGx+%9Pmz+6s>0c>{gptHC3NZ18IvZBL%n+bIUzDUJPbTfy!eyeIK!o-6pJMJm!EO( zU|m2y{d6vVS+@~oj&krEfh;S@%@Gmdm^SSt#K(83nkibH7g+Gs*C;D@sE8a)%<0lh z#06hchM1V%-a~F6p@jU z7&h$MSJxgnl7_UjL!gWSl&U=H3YNHUwF(pD=H_G8tT`r);axi`bHH&tLPKq8=0XA{ zO{&~iNlxB~D_2TPh?cv#Wc)ll%!=W|9|Ztx+rAkeeK-$AMMV&JK|SzJC#TbiJMM__ zn=k=D>(;GcwFaZ4w8ZS-RTxE3ET@Zx-iohGc^N5xq-rrOS!QYvM~1iFdL3=r#8ut5 ztLlx%jvd1O{b^ooYwrdDr4-$|B~~e>P*vK!dpiysIEb*Y&?-6cIxj1#-&S_DmfI+9o&)Nbd-DL7mpx>%2*eRluhH$KXZ_sor8})n(OoV`T1E` zv0^1^)vBdB;iOWq>DgzW0st&r_yxZHdWjYykX&dkGeiA8-GmyFASh<3EXmdWZc55t zJoeZ~%$)g8?A*N@0xy_mlu{XAfBgkov}jYkF;#s4sW^7*1VTbWv@??k(PvF>ps##h zt5z-a=+Vuu6S!_&vKGVerrJ%%&$z~7$Otq>(HWR(sVdJ91RibMUiEEd7H<~OW2qYU9uVHh)J0uCR}K!_=Z1`i~VKsl&x z)vDE)IrA;Qa&3~v^nwn^y|=dxfIF8l@|At8_?NrUtg6FXtz#Ib1EGPY(~vnn@j z-?14xckYJOYSnfyZ$6)Kp-W54kea&B@VP5Kz|<5GJ+jTfz=0sxYDHp~P9DMp0H1yK zB`j5p*SKN^cDo&M9ol2yp#B&%s6RweROgtWsOTbg?b?GqyHjxL^eGG(axbn2eZNEr z1`CxOWKda3mL^A9)WDUrYDDJr0Fo^v1ofk%y*}&BH)o)@xL9r2SvAVZ$<;EqCAUph zhlQBwU3kv_)j};>wlE|jSKnlGxiF$6p?i<6m_Ps1t20BZa??j2&QlT#B^j<0(!DOX zH!ff>U}=WU)b$B-xj76$K|vTeu%A~r^W@1>kR@3KA&4tZucYCs;D~~YFi-|GYTOVn zO&*Va{k&&)$HV}Mi9IoKA~0^;SPUI{?N7}}PF{_Dd-owUJWP+g!%wMumC$XGbx_+a zjs^gK{&^DX)+Q@~T!|V$5vHXy5dts@aHqYU%v^2Bf43~ z2*5!@p_!Xtd(&{8J15|i`JY|AUst)`@)P#&Pgj5dEp>=ix|iz674x{@s(H~9EESe& z*suW}d1QnaCw`@Z9ti@p<1xI9#0BBii>`x#AP9J3ni$c|Nll%1P?Kd6}s=@@p!B)4O$jiP;6x&*hFuNwwm;nrEu(~Uw zf#Zl`Hb@elc;atfV0v>+&!5TS~*B+2Kf$;F~>LHZwK7fW~ zyP421?8vG#5w%fF{rb`9+SN0|;_%^p`0cmd5Jgel?S0 zWeSTW$ggvnlr$8RCQnt&$!eane&1FkjIKH?%1&zd3pI&+PxTKs9mQP zx_0S;;9#o|g{WZ}W=__0-?0P% zW5$fT;e=`F(r>`42xZO8JWoW^Xvlag(F7qVDJjK^FM6Mkv;zl`^Jflt!2(nI7BicB zTGg?^KP@gUM(x_QQJpSyvMj@D4f6ZkIvoxKxdfG%trc&@WP~U|{Leq1k3oa{d+KC` z0yTH;XNKL&5WwYB>0nGXS2>816L_@|fF$fO}S1k_Jx2Dukb*Mjps9^fMd7t6I2mj*Nx$WAeGye1Y zAFv35n|@@54P1YhQo9|iRxPWrfYI*)wQbu*96x^CdoYf=|0e}wKx}Ny&8+U-J$s8U zUi=j@GBObn5w28cD%+B@zsZ~b`|kU&*@E>&AjCtfyXexT%UH1LGu)`uqD33L{`z$M`s;R_KAnYQ z$4=r5H?-)PzfSbctyBP8`SK;o%SZbzrZ*{u&C-7r+`6WP)nF`4p5g z)TiVugvl?C$H((Ng)P`-DvPg3LS-uZttHyZl|Q4nxL7@x ziy3-3n$wJw{7xV~J`RI?05sQ3pys^y4uXP$3?P63h=Qr20E|+EgoGd|$-hUnELgA@ zcDr3I4}p)CWV+2iWXQc}-8$}u6M{vHzQBM1{m{5^%d2|Mh7D_xnRyhHF|aDcu)@Q` zFn;{sys`I{MMDk^zMG*WMv^3S@74_o?c>2H1L4d_WVyouDpN>Qs&daOyWNfx zCr+YIpMh63C+pU&GQ=kh4S*PQzEM$V!U%YQ$Ky{tcFot^L!jP!??YHD7Ci~peJ3+y zH>{gkh*tYCGib?Ck+b^N3UKzZa86DzI+Krjd~tkx^zNxbhO@o zgL!Z1>4r_fm0`db!_i~MaQ5tZHB*EcfFW}+B?y7T;l$HVKjC#vHf&gf)V=#Wv-N%C zF=Lx%wkmNqx|a>v$u0S+8T zD(xC2NfP4X+GEZf@3Fw8OP8alut+;J&KxKeSl2elCE`N!Zp95(}{bMuFrvYnm`>rnvNfTT&a{lL-q#x zm~dLB(}^x!I$yOpV|KiEsjp;>L&$)eS6l-8`9^QHtjW7tyWMqGr}9pZQM|4 z1)jX#VZvD7qdde_IqjOS|NJM#>v;C;*^aeqlQkiz3N>xg6j6;DgOw>@1SGRS34#mh zlT3M=nmK9AY*G7DFb3?}y$8pR9rWtsRfQ(MG!;Qrs5~I5(DMS1$&;pBUx6AkW*qYK z&-!d&VzjeW936uPC;4?S&p$s-vx%$Xc-M`L#L%I`Z#ZGvx${@8G=NpGBY3hPG4u@A zfge5A$}|Vt_$X17&@sLPKA!u&*YTV^dj{{k^BxKd3*EX!`6jE?ibo%QSS`^vq?v14 zO2&{O_hQbR4|RXrplkJXKV~TORNmymg+g31l%Q9K-hFo#($dml4feh18ZADD!+}>{ zoqFBYUI2hsUwsAJw(Y2pnT^%3V8HFSN1-=EGPzK&+e@$C;;&oxlWXs()?Y+K1lzD-4f6+0pFX8VV|eYgY3xR% z&Ye5e=zXg5*SBvUHgVz$tf=U0g=2a4*=HD0+NLtNlIq-^Mch6ogs@<1Fk8236=Sl@ zSXr6!XIKB^GR9aL%gQ>ceupqZls^#t-@g6;u;IgpRclUv{dGON{q`u+ZxKQmM+hT? z^ZXr7|E&oV#$LCzzoieY4p4ynO@R{(0~DZuKm{m3HzxfbT+^Lgjv6p$00000NkvXX Hu0mjf*@>m) diff --git a/companion/src/images/library/00005.png b/companion/src/images/library/00005.png deleted file mode 100644 index 34e83f86014b84c8cd9c26ebfa5bd22486d852b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5422 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 diff --git a/companion/src/images/library/00007.png b/companion/src/images/library/00007.png deleted file mode 100644 index 358a29faf33932e53bf71fdae1c402843152dc20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3439 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 rF?>@{DLvV=e1I|;)| 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 diff --git a/companion/src/images/library/00010.png b/companion/src/images/library/00010.png deleted file mode 100644 index fa973998b937a976a7e7258daeb9ec80234e9e75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3215 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 BJRSf5 diff --git a/companion/src/images/library/00011.png b/companion/src/images/library/00011.png deleted file mode 100644 index f5f61be960f043d7a5b8d4efd62a4780d596005c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2171 zcmV->2!!{EP)pF2XskIMF-vn3l0k)Zl!To000OYNklVh?gdP~!f(u(PQ2GgK8s?&5DjMdZ%l1;UTxvD})zU~B&BWe`Kc0Q2k)>`` z)w%cFuhZi<_x$;;zwip`71S%JS5U8@UO~NrdIj|g>J`*0s8>+0pk6_}g8E}uJa=#H zUvJ}^ueP#R6PT{w-4@^2*V_88|M+TtTK)UeTRwgK`1b9UyRH4!e$k6Jsg*0szxjG< zQp_ckvIMfnkc;x}rKnR)P6O<&S{m|mzp5URrSycOwiO_T-S+`?hAY$qEbgjy3{U!Db^%1#bQStM z2M|otcLN2qxrXxMFNwHq1A_sC0CYmfb?pHc9>I9Uqt4tS2zJBw1O?Pmk32+vD{RDc z(tLP0CToZWcnd%&O?uR@c#H6eUHZ zZxK}VsNB8z9zTQ|8<3+0g@0D%yz)}1xGz-S%pfrFv& zh)^SJ`hh@!zv}}^N|Z>M1r?H{yq*DUGdCjR%Pg~S(9OevM&#j#0>#hKt!`O(Lqr^+ zN*bw3CcL$phv@zj3W*?y2t|t+^OK;Ohm>?+Mqk{N0Lp>w*(d>dVfGBlT51eHy`20s z&^mD}FH_dxdw^A0xFpn>ci2fngIaip83$5|fHAk?Ad&|$r6)j@*&~pp9*ONXmZfdV z@blrUyD{E5qm%E4B*^Z;Hde z`aMBXB?zp6_I7ph^>ytJyVq$v{u+q3dY_ITqni+4?~W`1C}v0dUU&-fwC!%)0j>5k zFTb~;r|$3+(K{m3;*f~L`iI>k4w<9{(vs}4tK+=i`KflWd8mRwtUjEbu{$=6=Uv!y zgV9tQK$#3WK8{IvQx|8nrK|AXKMX)LKZIByNl6NNepul6RZQFfD+iE;1I%g3CL8m) zL9_%gWQZesxA}anIxfaMcU3>su9JEFp4RF45Zc<^vG__yvqh*Su!MVa1QGa>7R{_1 z++Fe$L!X118=!Hp@O1;Wxj&bc?Dh!iJaNYQ{Y48t0PI{q528(ikO4NW0cuO@M_p0g zY~#G4Q$TC7Y?4y)<_i-$mR`M-(m)e6p77KRXqQ20e^`jLGpP3Rm$Q|V459P`}0E-J)JCUb9aQ9F1s4r<(01UA;ODUN-`U0eF}ka zZIx1%nS0oiV&*la;I<#b@}==3ZSJ70&!^}p%Bw6T zBcRpaVAi0{W6#vUK?)fJ zDIaWb(FuxMKc4i=xDlTTBxJ*Q#wtb(_#hupJ&$O3`Iynp1>&5`J*OR6c=;#*Qm z%9*eQM9$&GQ?jM+g&P=31?IAJ6pYsulenlo^}Kn_=~=yHdo0t30vGNcRBP$d8fkk? z5gP``gLy48mXZ;1(6W_MtHqKLAsIKo=FVZM)i7SCQ7I6+6Tq9>I1Q-$ypZ2JPY1Tk zquWYA#47gbvI}lcStyaxImfe@ zSTzA@yaibI4mocHMqDYMYR|ge#xdGlYHy|d;4iZwLTIT|wx4T@5SE8@8lhLZgNHjq zov-yo*QC@cK$GPP_-Y4`_qWI`<>65dwBSS! zuJMozYCPnG0!pG@Od@?qe;`%s9qu&YowAFh`mT}pSB((UXNnxPG!||2O?d@yk(?q}~1f z{r!KEBx!hl!D;_x2lz0VOp?;>_xt<%`#1L-o0C6u<@Bh%KKE2v_0FmjZSLi5^Q`ULqc`bdeu_^QBz7L6i_`32s;-?VAhqP!bAqc%+o_p5>q5aeOEj+?wwD>|?;SLco z=>{xb0_w`nIZkere)?k_+iXw2zILr69Gjbu)0ds(*nGbE@&2#Jdb{+q*HhzXTt6VF x_&Eevp7iGd#cd^T^n$-e_z^+H%=DrK{tNwprlt$IQtki%002ovPDHLkV1lZ`K>Ppz diff --git a/companion/src/images/library/00012.png b/companion/src/images/library/00012.png deleted file mode 100644 index 8af4d4fc427efed0117628f73d0f129f2b63cc59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2474 zcmV;b303xqP);Th6+V9}Y_0+TV1*w0wTNPnTF$L8~_$(3jK)N2==ty=kO=8TWUO zRP2f5Cca_h&GhVe?;R=iRP?tDm3jxHk4(SlN&?Wo9ZR^`FBa*YK`GG%Kz;hXwOMNi z6o8M6luHhP!Pfd6vY9P!$^a!s{((f1-~gIH`FiymaRPlE>&5_=@mG<0XHX7-t;ro} zc{ZvZfSLN<>e^w1J`$7wpt0lv(EG~H9EXAdgm+zFe*YY&zc*0;AtVP$FwM^7z?KGO z9%mW$Sx$d{MPUNR2`y)5kziPu$;lFWrS$x!`qzKo6HxV6HSo&ls0IM2y#Ic6b;iO3 zRh6o$!c_(TUVY~9y<&Ch^2z)5Q**)Vw6nn=*6lf4E5#&0#snkekTnfIB2surR}U6jql&06Yb2-mMlVAf<@5A)u6!1W{C$f)+w! zmji{krtPAF9AMTw$6)y~eYGQyT{ZyP?Po_7;r0%AZ z>2x~1nI!<%lj(FiolYXU$#gQymtRjNH$WQOquYE4wT5s~~ZuXl${$R{_z6HRq%l&}5$WV64Zks0xRtHHrl1-3%4q%G1x_Onsn|ZoA(n?juGmODJ~sY=b9Ztz!J!D*;OrDuS*1w`Ub$A0L&8regyyn zaCZ#=%WruR9*b#HchRhfiX0iMS^Tt|eu9BgiVW7gzEA#y5{-5Pu&J*FbfZCcVujXq zE|qzjJ?jhtmiBJ4)e=BjM*!GW0$eof7=l9pAy_a9(l+}*B?TlmtLrmp_7ectHWplr z!2z?0NC5B~0-HQ@2WCHZGp8@?^~B&=#5nFK+n3$8iJd~W*)0Sp=Nv#5#-}@2hx)7& zpe?8}v1U2?)Lrs^yYOi{=SA$H+1G9Q0qPbfzWVNZ#Cdo4QC;^Pf(ceEJugR2MRS9K zQaJBa>yLvC>)ZEn-2-5Ic*ll7+;b)Ult(#|1CDL==B`QZ$q2|jc}~E5Mp9;>^(>wn zDWl*qCvR7F`8chzzw2$i6gM+quHD5r$xclj0U#W8(b?KeKnoRpmnFOi6GTveFRgif z3Vs5>5P{`aqJ$u8tYE-c^z7^IOq;$qrRCe2lc2_Pm*d8Fw-gvHNa?X~6+ewmxM=j{ zX@Boo_G=@MZWgt=4ghn^+V<2vch`HTL*ca0oE7ab`+5ZMyay;GlaC?~)OeslAUFV0 z>;9FnK5eta=Vg&rPY3YX-p1J6?q$(4ef%gayC)Z=xo7f`TnzFpmN5(DA*UhL^{>&h zMN!;^M@n8Cn`Gbjy>E+Cy<3YFF$zQx3Ymx+YiNW^9q1-*8oGeiNeCe+Q z#c%DF8CE!`Iyz8wqXfzuC7iB~vs{wG_V=Adx_vy0k)YVQZDHlTWiaHHHp7bZWqM8e z0obg%i6+;NLF!~W33-AHC@I|nG z6O`{V-ySHx(SAQ=-?%-MiE#u)v)tm-NF)UMXr^(uLCz;`1oVQ!HTenPyoVgva=-rU zfy!-Twpy3#EFlU<07}sX5SCA+1v7>k>Y^Fl0Z|+S1}axNT8`iGCQ7pcRd z8zTT~cRJ#+F2@K^){Y(?pqyn?neF2w)%P^)dW4cl7lTBqP>em}mrXAFxM{&uge#PZ zjr(;$u^;=AM|mMfd3;AD3qyTsb6pmck6O@ke@j;&k919auiPAuxTqa~g%wY7#zjGS z7OtLCPfj4v>Z1ZE_FlYC0Tds9?g|3dPwtN1ToP2qBzJh`Ynk!%j!&J_utOOD2=PGU zShL$o_B~g;ASgxVFu98)Kw5aVlzNwt8{-`-4F`2upw&hK^3=0!E}DKZM@tf%G~ySK z3KeD&$EY!QXobiq!bM1E7nj{r$q6JETp*3C!^`5^i;oXjJ@HP-av}$zF5dLTK&c~< zT%crR2ruuCQlBuYqyrj+kd%{IVXH$g2WpKh>j<-h=IKS@8vRpl%@M&}AW7{%9CW$# z^DY4-UyW$1f=3Sxqm>3`qww%CrHh_Vl|(svSN7Q)W1KMi@Vf|9ToB#j-07*qoM6N<$f+oI{VE_OC diff --git a/companion/src/images/library/00021.png b/companion/src/images/library/00021.png deleted file mode 100644 index d1c867df1f20869d6736f9150b5e8c068ad1a7c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7277 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 Hu0mjfjD`9% diff --git a/companion/src/images/library/00022.png b/companion/src/images/library/00022.png deleted file mode 100644 index 85ef578f8fe81f8653ecd29ac371109d099f7358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4962 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 G2~7a+5VJb~ diff --git a/companion/src/images/library/00052.png b/companion/src/images/library/00052.png deleted file mode 100644 index 64eca2f6622431d42cc2fe98cc671ec54334e97b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3469 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*Qf*Zs5hX#1CLR`=Ysy8gRJ$k9D1HV7KUGkyqwI>m8@}dqp{&Z|7^4S@O9&ev2rN zV)n(_9m*RMoxkzO3bRHW*jns2Z|xkA1@fA+rNF$_uczjRP5ndetVU-MOv$` z`UFM>=J%*p$~V}1On zC3`zAn+hwS0lBFqi3%0DIeEoa6}C!XbFF}Ekg&dz0$52&wyjcxZ-9bxeo?A|iJpm` zfv#&sW|@(a9hZVlQA(Oskc%7C2q4c^DWjyMz)D}gyu4hm+*mKaC|%#s($Z4jz)0W7 zNEfI=x41H|B(Xv_uUHvm1jG!N)Z*l#%z~24{5%DanTbjHr6smXNP z3=ITeaOi_%^bPe4^pTa9WTxRz4w6AA2Whqn$gD`siEzoxP0cF?I>A=S(8vO87(zA@ zN!H8+ED18m$}=}HJvBTtr6dEW3r*M~H8VW}=nbHHeNbrWLxT$#`ugzT2ZaIc!Kfu9 zh4E-`jRqGTqK16Ns32P*U$(!%{4%=Ohe9m%cXFEE{NwI@_$E&K--4*9s zRxL1MGoKmfqi1$?Y2>xpA4?y}MsAH;duUfw?+2OOgI@XuEuD{wxmO?#~Z5CC-UzwmK!ps??>sbwl>aEW4!)Yp2?VUc7Yc z6NkL{Evwr8dgW(R|2eJR$PqdBb2#6+m@bxulNyY2?xwC#(%o|Jwm64a{_mIEl||e3 zNKaW9ohhdx7@F@Kw_HR0msCNOapkWzZKsPlr-azIyQugw-<+hhYVAyK8IOky>~?D( zF0DS_`a56i(BIz8Uf&d+exG>Q!aLf)@sCEq{fEL^-f;ImPCD>};ndkMUJjw0bI%#G z!khY1U5bi1_8Ri#wU*y_dQ?bYiTl!`&GC)iQ(p?P+ed0dZ8c_$i@s-+D(sl=vVXsW z3df(|fcV*Lu2(k*xm;$JV`bVmakNrEbpqyAOD_jerbdfXXdH0f8LW> zk1vcnzQ_56vfc4&X2w@x-XZ*QjsH$bCCuFV(Es*-e(_HqQ*J-W-r&AW;a29KbXk=K hmJ7(q&+O(u20lU0^PNAp%mk)u22WQ%mvv4FO#qlUGS2`2 diff --git a/companion/src/images/library/00053.png b/companion/src/images/library/00053.png deleted file mode 100644 index c73a997018e588451bdf45c8c1d740f5ab917342..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 425 zcmV;a0apHrP)Rr00AjUL_t(I%gxfiYlA=(2k;jJZxLQchc1QEIo&b{`BxlDx&*3wX)O*e zg0oBeuUHC^g5cV2_6K!M=}-f~i}x;8y(L2%=)*xi+}->7Jb--^_Wf=ToJemR1N`PyJcCO|3E3fmfHFa zP}>G57)N9jOw2<>Un6QF`gTm7dI$ah_EJgz&vh`Wn-{&ma4g_DkE}%=+=riWaT+!- zn`g5{mL<<|?2VtcFmJ)>flB}qCN0pGi_n^6X<{!|o;Vc1kV(3GWypaf@V{#V0J_Pe T@aYwq00000NkvXXu0mjfRS&14 diff --git a/companion/src/images/library/00054.png b/companion/src/images/library/00054.png deleted file mode 100644 index e4d1878c923b1146164338789ae4317529cc06ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`$P6T3NW8THQfvV}A+G=b{|7Qd4_&SUQjAI7 z?k)@+tg;?J4rhT!WHAE+w=f7ZGR&GI0Tg5}@$_|Nzsn*c%xbw&v+EL2=&GlSV~EA+ zx0ejL4jG8BJ#bhUDI;Ue;VRI-FkJHmV^oZ5u+^UEifPmMj;;*PSowz9*@O1TaS?83{1OU-+e%b&4 diff --git a/companion/src/images/library/00055.png b/companion/src/images/library/00055.png deleted file mode 100644 index 5bf13ecb2db9a290b2c6562947003a438e6da7c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`$P6T3NW8THQfvV}A+G=b{|7Qd4_&SUQk(@I zk;M!Q+`=Ht$S`Y;1W=H@#M9T6{Vt2NIG4)#%&dh#p-Y}Fjv*GOw_ZNTd00V&E#ax` z8^t5p{H<#)Ffy&X!pQXTnrM|*c%g{O0WE zv&N<=$&>M@o@=sA^P>Xgmv#32N`@y&+DyK3K8RCmnEm_o9-013yPLnivRO&keLGgO RtqbTo22WQ%mvv4FO#ovxdFKEC diff --git a/companion/src/images/library/00056.png b/companion/src/images/library/00056.png deleted file mode 100644 index c44d1a5e0f48e0b4efa0f69bc07065c5dd77fecc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 603 zcmV-h0;K(kP)~fWyrUZcb(y|_qmds8T<0^AI5jSjrUKo3+<<%3;p3X z&Gp*wH{6=>RYX+s5k^UQpl5|1w50@XuX3t1`aqF_00004001BW0003100;m8Kmf4m pLglRgkNBy$~r0002AM!zfkLA7SEnu-7b002ovPDHLkV1ndf1^EB~ diff --git a/companion/src/images/library/00057.png b/companion/src/images/library/00057.png deleted file mode 100644 index 678355ae01046bc6cbffeb5154ee2650c372f43d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 634 zcmV-=0)_pFP)|ghCdPFCGFgbg>Lc(dN2fR7u@u($Og1kDT= zovtbZru$vw_GThr?df*nTOttL+sSuJ1cHv8tSk|5xxb&>^equENPmrc&n*!kIRGlq z02%`<@S>{@CQsEUvBzN>xg%rxKtXX3J-)ITxb0E5ejNHAU_T_ zu5aY^8b`=32CgzxvkwehKmgFi0)fH+z{(6i;YF-ikHW;WY@?f=aar%O|GP)&>F*Jg zUjQ_co_G-AjF-L@QV^VZFkkMX Uh5FI?1poj507*qoM6N<$f|Gd{F#rGn diff --git a/companion/src/images/library/00058.png b/companion/src/images/library/00058.png deleted file mode 100644 index fa384d1e69d6717e7dc37c1f73537364befb0c16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3760 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*Qf*Zs5hX#1CLR`=Ysy8gRJ$k9D1HV7KUGkyqwI>m8@}dqp{&Z|7^4S@O9&ev2rN zV)n(_9m*RMoxkzO3bRHW*jns2Z|xkA1@fA+rNF$_uczjRP5ndetVU-MOv$` z`UFM>=J%*p$~V}1On zC3`zAn+hwS0lBFqi3%0DIeEoa6}C!XbFF}Ekg&dz0$52&wyjcxZ-9bxeo?A|iJpm` zfv#&sW|@(a9hZVlQA(Oskc%7C2q4c^DWjyMz)D}gyu4hm+*mKaC|%#s($Z4jz)0W7 zNEfI=x41H|B(Xv_uUHvm1jG!N)Z*l#%z~24{5%DanTbjHr6smXNP z3=ITeaOi_%^bPe4^pTa9WTxRz4w6AA2Whqn$gD`siEzoxP0cF?I>A=S(8vO87(zA@ zN!H8+ED18m$}=}HJvBTtr6dEW3r*M~H8VW}=nbHHeNbrWLxT$#`ugzT2ZaIc!Kfu9 zh4E-`jRqGTqK16Ns32P*U$utUq8EKqtpj-^DjP|2ULIUp84hv*;z?pGpR!aK%+GE5bcqtFKbOWH&3+i!rr9 z=1kb-Lhf~%^Zi1)FPvt&Vsq$iLuCDqju%VLaP_kK%s)E6Bc`|HhxmDpBYbVzHEFu1 z^y?<*)*pN+r*Q7Y;*;CT1rJo?A4_lKu6*?T@Ypf6_cUv!Y)5 zKy9Sj8~4uH=fW=b@s+V{U2wl6{0}qBh4}?53jg+Rwbjhgp7i@S*Mh{P*jveUIbi_~ zkFMyx{3A9wC(!Ei+f&-DE59}c$-hsXXl3)(!DVN}?)~8z0lOn_u6M}QzEYp%xc%n6 z-N(;g2tVt>6m4^C=Tpt9Ccmw0A1}zPDg3=y+mKJ-y?w~?BR20p7zGwMo(y8>Vp5MN z4>tc}6so*oM+&R2oYaMYG|fp(H9vV(Sw8NN{IA5*UU5wC>g{*EMSoR8ooDuk%~$^u z^L*d&+Xo)b`dK3U%u;Es@STP3lfK8jdNYGZ`3u9w?N5!~YGnmy+?zTp{Xu;n_?o?bCe^KC4Rt~2zzdPHPd!IInRmZjVF9j(>&t2{8#g5O}{jvoalY+~cv zmD^_cv48(+J^fz6k@p7myx-?{EohTc^O|vR`DM)mNk67{%0}=Ch5k^|G#B~vE9|#v zG1tr5?(cKz($c*>chCJ+fBMIZfW5zG<>n|d2(R6~Y8H$3L9y@5-}sCF_Wj>2FHkF1 zSp93a;ic=xohn_c4o->BRGlVoAM>T9?s`bLUD&ZbhEwjTNCa^)6|Cf$v#I`H+RtCz zuQP*^v>VpEUU^tPGilq2S(nP*`EHmvX^HgqzWFnhCY`+Q`A+`eFLA$}S5E}7iU+cZ zXq9~bWv=V$lOJmvIBQP8UXDlV{2Lftf9%K&uiMbOFOoa!UgZ17cl2<M?s9>M``&~-il000McNliru-2?~@A}>_EeGLEr0Z>Uq zK~y-)t&%ZL!$1&4*K*+oVdxRLM%*M6T!A`g2u)-O;oN{43B@hq3N+i&;1eK+3m_*V zB$ga6e>~1=$4-O9uSQQVKeM}5>Yp+&O^UyO<$a)sr33WHm*C-Yn(bl-q>EUB@q3(= zkpNR2N#N_~QGg&T-vme`NEZN~Vvi2cqeMq5ah48nPy2$T4AwOFjc24a3Yw31JSlY& z7=3FyE|fa;&0H9vXS}s*gsS$WF|U0#u8>I;8hsZOH1nna%VFqj%D_1bKyfx;;~Jrk zof?1BYz+W5&JpUURI;Mk8nrUA!+1t^0I+d`O!y}onyrCSY2a_AVfQ!~SX~!;$ zU_-Mta6xR$5bD4cZfUlLz4b!}fas|4P;Ahn!l>|+Gn)I~$9`|}oWV9n`bN{y8pk_& t#uw6pADSCO^XD2g#%m|abkcOV`33v%2Zo%LoI?Nr002ovPDHLkV1j1%y88eC diff --git a/companion/src/images/library/00060.png b/companion/src/images/library/00060.png deleted file mode 100644 index 9f3c5d78e040bf7ea81c74d87f2edabced38ae62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 764 zcmVM?s9>M``&~-il000McNliru-3ko=EDv)a6A}Ob0*OgP zK~y-)b&^47(@+@4zcs~99Lp+#V=^=FGQ4aD4`aBcLSaWwpjxmjJ{y!2l#D zW{4Y^g-b+6K|TafFffuLloSI%#nQ<5s`B-^Y>bSyc>R9LLO{tA#y1h~NgaHER$z3F zc@>8n7QfJw@u)i$7?x#r-hbpq|3e}X7+tet2?L)b0>bQK$1@Ccu zM3vB3oR*!<~O3MQ&7Py-SIpJ5q3dn4D*4lSKxEP@4!dkp`1TauKoj%`met7A0WC%n*w%s`jB0*jRE&nsiZfyXHej$qRi{hRR zzQ&cqfu`Yi4V!rg8;Xojwntg50a~CuYB|4|^Y0d;vrr_u`&4%cMudig{NQDNpyBYe z4Yxrr^NI+qP@nzc{!MJP{2`$;wtE{1b)gUmkM0z(P&gWO4DIR?BfMi#ju!f#6rQ6s zA{xJ|G9$HNKZIBi$2l0aYs4ntN8%5Ic}UP>-jW(6;~XrSM?1ASVegW7m5AttLVopo utk?UOsE5LOCLR`=Ysy8gRJ$k9D1HV7KUGkyqwI>m8@}dqp{&Z|7^4S@O9&ev2rN zV)n(_9m*RMoxkzO3bRHW*jns2Z|xkA1@fA+rNF$_uczjRP5ndetVU-MOv$` z`UFM>=J%*p$~V}1On zC3`zAn+hwS0lBFqi3%0DIeEoa6}C!XbFF}Ekg&dz0$52&wyjcxZ-9bxeo?A|iJpm` zfv#&sW|@(a9hZVlQA(Oskc%7C2q4c^DWjyMz)D}gyu4hm+*mKaC|%#s($Z4jz)0W7 zNEfI=x41H|B(Xv_uUHvm1jG!N)Z*l#%z~24{5%DanTbjHr6smXNP z3=ITeaOi_%^bPe4^pTa9WTxRz4w6AA2Whqn$gD`siEzoxP0cF?I>A=S(8vO87(zA@ zN!H8+ED18m$}=}HJvBTtr6dEW3r*M~H8VW}=nbHHeNbrWLxT$#`ugzT2ZaIc!Kfu9 zh4E-`jRqGTqK16Ns32P*U$Qfvb)v2(V0ce^;M+zg;0IWYa;5wZVT^a;wdqsVDl) zV!}$l)veAPQEqe0!u|v{ACr$;E3~(h&%r^0gE>*5r@_Dp$Wm5#<30Vb%!E}tgV!qU zD?6_B`l;1|tdf%F*8Ptb&0X5K!JdsFG1}k)<2Bg@Y)ivJk3H9UwZ&Az{nO1qrc2`@ z1rPn?pVXos;_$e~; zTJ*!8u9t9hxDnCLR`=Ysy8gRJ$k9D1HV7KUGkyqwI>m8@}dqp{&Z|7^4S@O9&ev2rN zV)n(_9m*RMoxkzO3bRHW*jns2Z|xkA1@fA+rNF$_uczjRP5ndetVU-MOv$` z`UFM>=J%*p$~V}1On zC3`zAn+hwS0lBFqi3%0DIeEoa6}C!XbFF}Ekg&dz0$52&wyjcxZ-9bxeo?A|iJpm` zfv#&sW|@(a9hZVlQA(Oskc%7C2q4c^DWjyMz)D}gyu4hm+*mKaC|%#s($Z4jz)0W7 zNEfI=x41H|B(Xv_uUHvm1jG!N)Z*l#%z~24{5%DanTbjHr6smXNP z3=ITeaOi_%^bPe4^pTa9WTxRz4w6AA2Whqn$gD`siEzoxP0cF?I>A=S(8vO87(zA@ zN!H8+ED18m$}=}HJvBTtr6dEW3r*M~H8VW}=nbHHeNbrWLxT$#`ugzT2ZaIc!Kfu9 zh4E-`jRqGTqK16Ns32P*U$qaB~dlF31b%G-9GvJKIn z!zg;uzNpg9nS*o7nnPQ}9_Pz16c^flp3RcY=2D~7ui_W4@~f&&B;Q=#l&OAal9YXPdxjBi%9pNsypr{Bs4eophB~6Hs|+iif?Dcu3(Q4O?hJ=%wVxI z>ql|R)1wm`CHP;ox_b&r@Mzw9!~DT7rtaSeqf86=qcmYSP?t}%C`))jc z*s!ijn7zj~;rQBDX_uJ*w7P@Qzjq1aifd~gc7M+`U&|1%b@q=%@@;bj z&aL~W!()=|T-a;k|5sGCuYFso+<}5~0&Vlw&RcYCyUhCwDLkR>=@MLNH_Vld{eQST zUcKwqPJ`I!`}G@YBp4$9X3E`|Sf@SZ<(!APGZ`2AU+GYlu85aSYwG*Vvd1iXo?Y<7 zgC~@01sS+Q8gBRa8f05e{mo%|^DVQfr&1E)@<(j{1wIz8VXKIKH&NAjKPB89w=GfzV(fr=l#k{znt?=g8DF?u6{1- HoD!MyD#EwYK=5QinkK@chI`c+0?=%wT=Uq=XE~xN^#7L|=(-{{IqtU4ZQZNW(M$kYQ$RZH{ zfHiR{%?ol5C2acfwAmGuLE(M6HK{To7+ zMdkMixOX-&?0>G#wQ{vnJ-)HP=auw!4Qbc5DYO6ipoHNM%XHPsN48ELPSjMT2*tQ_ zXE$3<#u$wsh4ytso&W%mmn2?A?sm2qe<~S)!&Ci01dB}Ll?niQhAbM+KL}*P{J;Pb zMIZk9W(OQb!t2A`wCqrJG!u|WvW%pIE|KllS`XeZ*|8Kxk^pH8AN zNmL4KCllvK4Q1-X;XEDoIedryU+EOaX9zqT$Q=c#jzA&*ZfBAR|HZzee6jB=#yFB# zV6eL>i40O0yb|=`>KZzq!oH{;e^a$JKZ!d*7%MU^0CXeanMB^I|CqJ}NdZLOXYl;U z9a_lGxOnuDf8pO@@V`I4z2vJHf5Y_+*H;nvD)YD9^$pio5%?R;-JnwkV|f4q_-6iD~Z zh%8^0)xp{+gmnqd{#cwnma(d1K-a%M)OI3DPvI9%yN|bt_eaBXf4slTR7~&DO$4A@ zmw;{PcdPqa6)6UypmNVuM(A;hDv|Vaulu20Gwck-Sy74p07N)58&Dpog^t`kC znpd1VHwE(7dgXLky&;PYg?wX~+}PSclC(%OYg+YCI_4g?1hWBWlMunCV0#hjnXOj z?viS!Q^AP0^xZe@4;RPBjmwIB-P#iJW@k*T3Pq`&%8fI>O)bRBNS^e4puyHcJq31O z{wc7NsDLOIHmXrMf7VU6oBdXw(gbVL@_) zn5u1X*J)Cj2EUSsa1PgjUGq_m3mE-eTRXCH(Bu2?ighJPovgjm{*mnmg`{6gvHP5& zA3hO5%dM$w9>iX>^$@)@A8UT<2(!hNhQ@d*HBN(+FF%B$EC|5!b6byjCIk z&`zn~Ys)>p*p{{OTQLPu?#{8|`ZcxvR*6HWXGYKUJ=>M!0C9&2T$3_sIS)Dw6bHas zCt-kq!O^$QNUR}ywhlcHyyqh_!uste3j820XQroaxsLY~DBKRcyQS^er9^Bj*%Qj) z-n;z_J2E?J7D4Eda~Mu+7)Vo5-?)fUb%||>oiWTyE^mE8mvU>BL~ydDw1gmcK>ONi zjYsS3Aw14LZYs=$PhcqTl-~a7j$=edW=K>U)u5>1PC>&Sn@qU z{j*1?EGO%g8qypm_xdvb+$~vQGf!2=(DU-s{iiPDtst}Bwl%lFfgcw>mzEh4E zIcoBgbO+rmDyBiN)6g4l;>DGZ%g#`vt{%!-8gSK{xKw(p!Nk2BN{9aLDm|v1EZ`=1 z&#P3lCD2vd>S#Z#$KPnt#3`2Hs|>;1AmUFYyIt))>hF~lhx{$Eca9I$4jc$fY;m8S z2uC|S7Rp@aZ&9ke>p|3I+kfQt_qB3}6~Sj86jfFy9WK146x%HXKr87s-A|oIz0Qqz zWZVY`mZV9CpeC}FZ=Tp(^uXjLk3(z_pKX_bzx>8|Sn^IBA zm$2=qs@^Z3YZ4M%^l(gDCZH1B^r-`g7PJ(DVehPT&2sU*TGsuDMx5+S#ZT$Zfi!?N z74(3AOHdZvosy`==@_U=QbS2L;v4x&C+aqSmDqN5nj(!K-DEVRpp}mxJ{&k>tKLoa zllj$WFzKB{Rx{ou=pLFVZraYQowU-MmD5!NV!m-nPKigt~xtI(lR3t+y@aRth=#u&b= XwlJGfv{%5+{}n4Ud(#?Y-?)DPb8?TN diff --git a/companion/src/images/library/00064.png b/companion/src/images/library/00064.png deleted file mode 100644 index 2b2081c886fb7876f9221ab4d9f8fcc9cf3ca492..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3727 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 KMNUMnLSTYNUeP%K diff --git a/companion/src/images/library/00066.png b/companion/src/images/library/00066.png deleted file mode 100644 index 2e7668f7d00a438677b0a8eb3ae026100efc8f6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`$P6T3NW8THQfvV}A+G=b{|7Qd4_&SUQjAI7 z?k)@+tg;?J4rhT!WHAE+w=xJb-d?uW4JgQ7;_2(keve6tQ(H{uqr+vOkYr{^M2T~L zZfo}TJs$;;9*M$m$mxF_C%>8 zMmzYdr-k@n~ z^pI(d_X)NMl~NfeR-TrS>RFN_7t+@3Aa^C~|2p+tt&N*)j9aFF{OalI=d#Wzp$Pz} CDStKq diff --git a/companion/src/images/library/00304.png b/companion/src/images/library/00304.png deleted file mode 100644 index 936732b2e39af666f330a515317abb1350b098d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 865 zcmV-n1D^beP)-#f zlI-Ah4igdCx^6Pgrx?5hzL^06K3)RON(ckoNH-!gn68g6A5RhF;p@s_p zNr*{@YsgLC5oB1KuDL!VU|Ik~Ap%WOO7;an6e7?f=j^8sP!mDa2{L}>(~SUX6f4^h zMUmoMQM7`O#zhSf=;3||u!yIdO(FvvP6)se8pUl00F1YuMF`+<;xceCwG8omKI#2_ zFZO0o0JGh=Uejjwb}K};u;nA7=_K`WbZqW8tR*1l++5pw-%IUtZEE}1@@3VUUkdi9 zPolSkjhvulGJ40%evhz)@?ph(#^@ML0U!f9n;}p(y;bQ~dRagsuxcleYpj;KM^r6& zD8sscpS)f#(Q&hMUmHh-B5+hmoC0OU=4i2q@Q?eunKX`C0=OdN*k$(ttZ%XIdO?L% zxPETk3|dkAppTc1gDe13N=-keUKOixg9WniviOX@m#k1?TPxV}q*-QkUSlu7A;>H^ zT?3$(42ivyvI(at6|ajQ1bLarwa-14V`qjb0dO4BZY7`2$R0rn_GS=w5b4g~Y=ec9 z8a)e4>-P8JJA$M;bLf;-ve&u>_8j0Az2KMTgj)*~Gk~-?q48Vf@#x}o1={0Qmw=T4 zmDog$Ig?to3&4?MrL_R-J%Px(Sgyy;u4_I2BsQ(E>RCwDdjjLRJ^Mr-;BZ0!hZ6!g r95n&l^_u){EX@E>0NNv#fW!F)dB(`*NdBM^00000NkvXXu0mjf5kiLT diff --git a/companion/src/images/library/10801.png b/companion/src/images/library/10801.png deleted file mode 100644 index 1ad61712b6e8a8299f91366c0ab604e3294b09cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 725 zcmV;`0xJE9P)7_*C zRDyGA*s!wJM*u!c;VkvA0={+(znd|~07s`B3?!I{F)^!GD6H1*@^N&>3_x15U^4`m zO)WSUUokM&>f(4YfsbxPb=mog!c#gOyi?c?NrurJX%JXJ_jLpmBQo>GqA3q7z&j-1 zEEPLjG~0!fk#+rm=Nl0!fztztqp!A`O&Te*a$s`gT8xyDG&%##)iPu|Wp%X^y5Wgr zq+m}6k_cgkBITPg^uksW7J7}_a)_|swVPGATCKj8-WFq;)d;g0Aw%B+Uv%WA(rOA% z?6SUfTTsibo@o$-^qQsIK3O#VUNNY092C(| z12i^IY_$@4paKYu_@D#p+Dzjp0QL`2*OiNP^TMHUQiMQ#w7cS(B**3wpa=-bN3Q4$ zIL^sC>d-pi^I_Qfp2IQ~fAN=1`udTNz(?RC@Dcb3{C5JsWws53g|0(@00000NkvXX Hu0mjf;cq&= diff --git a/companion/src/images/library/10802.png b/companion/src/images/library/10802.png deleted file mode 100644 index bbf26657e6ca114c53121f51e24fc9c12866b04e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 881 zcmV-%1CIQOP)ZnSmb#DYP~fno!H*Q+xh2&)m<1?@pKXV4KUra*#_&MV_x$xa1sG$y(5aIdqhXV zu6;NtqmjWb;qWUEI|Wf3CW2mwT_ z^5<5Rx*Yk`Jnf26raKJ~%YZ&d1hgrL+!6GK>FExYkoAM>0lY7{fq+x>odPpjwe1Hs z?X#0!qk%ZD1=S8m{Fbq7*Xqy1G600g4BJ@?h3Eb<0uB49`i+?~wW|Y8Q!tR5_7%1O z=(T=}{)e^2vo7&PHCeALVUhZ9y36X!1V3|NZjBgJ(qdd#w9T$zb~ekm5SX;zpw90c z0MS`0RJXz(IMC8Y-4%hpA}_UMQ(HiPq4p>#eHTvcS6z^Azj-I^iIAY|$_6!&hauo_&)9}zV|$L>A>LV6_Ut*;xfvHL7qFfMi}qRVrPoeWN>&~h^i#HRx_-(2xLVth;ii2K+e!SU98cL7|IqNc8UK0V;%XM$ zSiesd@c+!a+Wg$fLT--@{}cYUqtDON|MD5p_3JqY;Ecw9n1DvNm7B4k00000NkvXX Hu0mjf(#Mr8 diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index f4a261042..159d3bd4d 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -21,7 +21,6 @@ #include "mainwindow.h" #include "mdichild.h" -#include "burnconfigdialog.h" #include "comparedialog.h" #include "logsdialog.h" #include "apppreferencesdialog.h" @@ -34,7 +33,6 @@ #include "warnings.h" #include "helpers.h" #include "appdata.h" -#include "radionotfound.h" #include "process_sync.h" #include "radiointerface.h" #include "progressdialog.h" @@ -483,7 +481,7 @@ void MainWindow::sdsync(bool postUpdate) if (syncOpts.folderA.isEmpty()) syncOpts.folderA = g.profile[g.id()].sdPath(); if (syncOpts.folderB.isEmpty()) - syncOpts.folderB = findMassstoragePath("SOUNDS", true); + syncOpts.folderB = findMassStoragePath("SOUNDS", true); if (syncOpts.folderA.isEmpty()) errorMsgs << tr("No local SD structure path configured!"); @@ -556,11 +554,14 @@ void MainWindow::readSettings() bool MainWindow::readFirmwareFromRadio(const QString & filename) { ProgressDialog progressDialog(this, tr("Read Firmware from Radio"), CompanionIcon("read_flash.png")); - bool result = readFirmware(filename, progressDialog.progress()); - if (!result && !progressDialog.isEmpty()) { + progressDialog.open(); + + if (readFirmware(filename, progressDialog.progress())) { progressDialog.exec(); + return true; } - return result; + + return false; } bool MainWindow::readSettingsFromRadio(const QString & filename) @@ -587,40 +588,38 @@ void MainWindow::writeFlash(QString fileToFlash) { FlashFirmwareDialog * cd = new FlashFirmwareDialog(this); cd->exec(); + cd->deleteLater(); } void MainWindow::readBackup() { - if (IS_FAMILY_HORUS_OR_T16(getCurrentBoard())) { - QMessageBox::information(this, CPN_STR_APP_NAME, tr("This function is not yet implemented")); - return; - // TODO implementation - } - QString fileName = QFileDialog::getSaveFileName(this, tr("Save Radio Backup to File"), g.eepromDir(), EXTERNAL_EEPROM_FILES_FILTER); - if (!fileName.isEmpty()) { - if (!readSettingsFromRadio(fileName)) - return; - } } void MainWindow::readFlash() { - QString fileName = QFileDialog::getSaveFileName(this,tr("Read Radio Firmware to File"), g.flashDir(), FLASH_FILES_FILTER); - if (!fileName.isEmpty()) { - readFirmwareFromRadio(fileName); + if (!isRadioConnected()) { + QMessageBox::critical(this, tr("Detect Radio"), + tr("Radio could not be detected by DFU or UF2 modes") % ".\n" % + tr("Check cable is securely connected and radio lights are illuminated") % ".\n" % + tr("Note: USB mode is not suitable for flashing firmware.")); + return; } -} -void MainWindow::burnConfig() -{ - burnConfigDialog *bcd = new burnConfigDialog(this); - bcd->exec(); - delete bcd; -} + ProgressDialog progressDialog(this, tr("Read Firmware From Radio"), + CompanionIcon("read_flash.png")); -void MainWindow::burnList() -{ - burnConfigDialog bcd(this); + auto progress = progressDialog.progress(); + + readFirmware( + [this, progress](const QByteArray &_data) { + writeFirmwareToFile(this, _data, progress, true); + }, + [this](const QString &err) { + qDebug() << tr("Could not read radio firmware: %1").arg(err); + }, + progress); + + progressDialog.exec(); } void MainWindow::compare() @@ -824,7 +823,6 @@ void MainWindow::retranslateUi(bool showMsg) trAct(exportAppSettingsAct, tr("Export Settings..."), tr("Save all the current %1 and Simulator settings (including radio profiles) to a file.").arg(CPN_STR_APP_NAME)); trAct(importAppSettingsAct, tr("Import Settings..."), tr("Load %1 and Simulator settings from a prevously exported settings file.").arg(CPN_STR_APP_NAME)); - trAct(burnConfigAct, tr("Configure Radio Communications..."), tr("Configure Companion for communicating with the Radio")); trAct(editSplashAct, tr("Edit Radio Splash Image..."), tr("Edit the splash image of your Radio")); trAct(readFlashAct, tr("Read Firmware from Radio"), tr("Read firmware from Radio")); trAct(writeFlashAct, tr("Write Firmware to Radio"), tr("Write firmware to Radio")); @@ -832,6 +830,7 @@ void MainWindow::retranslateUi(bool showMsg) trAct(readSettingsAct, tr("Read Models and Settings from Radio"), tr("Read Models and Settings from Radio")); trAct(writeBUToRadioAct, tr("Write Backup to Radio"), tr("Write Backup from file to Radio")); trAct(readBUToFileAct, tr("Backup Radio to File"), tr("Save a complete backup file of all settings and model data in the Radio")); + trAct(radioGetDevicesAct, tr("Connected Radios"), tr("Get a list of connected radios")); trAct(compareAct, tr("Compare Models"), tr("Compare models")); trAct(updatesAct, tr("Update components..."), tr("Download and update EdgeTX components and supporting resources")); @@ -897,7 +896,7 @@ void MainWindow::createActions() editAppSettingsAct = addAct("apppreferences.png", SLOT(editAppSettings()), QKeySequence::Preferences); exportAppSettingsAct = addAct("saveas.png", SLOT(exportAppSettings())); importAppSettingsAct = addAct("open.png", SLOT(importAppSettings())); - burnConfigAct = addAct("configure.png", SLOT(burnConfig())); + radioGetDevicesAct = addAct("configure.png", SLOT(radioGetDevices())); compareAct = addAct("compare.png", SLOT(compare()), tr("Ctrl+Alt+R")); updatesAct = addAct("download.png", SLOT(updates()), tr("Ctrl+Alt+D")); @@ -910,7 +909,6 @@ void MainWindow::createActions() sdsyncAct = addAct("sdsync.png", SLOT(sdsync())); logsAct = addAct("logs.png", SLOT(logFile()), tr("Ctrl+Alt+L")); - burnListAct = addAct("list.png", SLOT(burnList())); readFlashAct = addAct("read_flash.png", SLOT(readFlash())); writeFlashAct = addAct("write_flash.png", SLOT(writeFlash())); writeSettingsAct = addAct("write_eeprom.png", SLOT(writeSettings())); @@ -1003,8 +1001,8 @@ void MainWindow::createMenus() radioMenu->addAction(writeFlashAct); radioMenu->addAction(readFlashAct); radioMenu->addSeparator(); - radioMenu->addAction(burnConfigAct); radioMenu->addAction(editSplashAct); + radioMenu->addAction(radioGetDevicesAct); settingsMenu = menuBar()->addMenu(""); settingsMenu->addAction(editAppSettingsAct); @@ -1090,7 +1088,6 @@ void MainWindow::createToolBars() radioToolBar->addAction(writeFlashAct); radioToolBar->addAction(readFlashAct); radioToolBar->addSeparator(); - radioToolBar->addAction(burnConfigAct); settingsToolBar = addToolBar(""); settingsToolBar->setObjectName("Settings"); @@ -1577,3 +1574,8 @@ void MainWindow::viewToolsToolbar() const QSignalBlocker blocker(toolsToolBar); toolsToolBar->setVisible(viewToolsToolbarAct->isChecked()); } + +void MainWindow::radioGetDevices() +{ + QMessageBox::information(this, tr("Connected Radios"), getDevicesInfo()); +} diff --git a/companion/src/mainwindow.h b/companion/src/mainwindow.h index f90b9ad03..2bc73bb92 100644 --- a/companion/src/mainwindow.h +++ b/companion/src/mainwindow.h @@ -94,8 +94,6 @@ class MainWindow : public QMainWindow void readFlash(); void writeBackup(); void readBackup(); - void burnConfig(); - void burnList(); void sdsync(bool postUpdate = false); void changelog(); void customizeSplash(); @@ -127,6 +125,8 @@ class MainWindow : public QMainWindow void viewSettingsToolbar(); void viewToolsToolbar(); + void radioGetDevices(); + private: QAction * addAct(const QString & icon, const char * slot = NULL, const QKeySequence & shortcut = 0, QObject * slotObj = NULL, const char * signal = NULL); QAction * addActToGroup(QActionGroup * aGroup, const QString & sName, const QString & lName, const char * propName = 0, @@ -209,8 +209,7 @@ class MainWindow : public QMainWindow QAction *editSplashAct; QAction *writeSettingsAct; QAction *readSettingsAct; - QAction *burnConfigAct; - QAction *burnListAct; + QAction *radioGetDevicesAct; QAction *writeFlashAct; QAction *readFlashAct; QAction *writeBUToRadioAct; diff --git a/companion/src/mdichild.cpp b/companion/src/mdichild.cpp index b9fc2c89e..02949f359 100644 --- a/companion/src/mdichild.cpp +++ b/companion/src/mdichild.cpp @@ -23,7 +23,6 @@ #include "ui_mdichild.h" #include "modeledit/modeledit.h" #include "generaledit/generaledit.h" -#include "burnconfigdialog.h" #include "printdialog.h" #include "helpers.h" #include "appdata.h" @@ -1434,11 +1433,6 @@ bool MdiChild::convertStorage(Board::Type from, Board::Type to, bool newFile) QMessageBox::StandardButtons btns; QMessageBox::StandardButton dfltBtn; - if (from == Board::BOARD_X10 && to == Board::BOARD_JUMPER_T16) { - if (displayT16ImportWarning() == false) - return false; - } - QString q = tr("

Currently selected radio type (%1) is not compatible with file %3 (from %2), models and settings need to be converted.

").arg(Boards::getBoardName(to)).arg(Boards::getBoardName(from)).arg(userFriendlyCurrentFile()); if (newFile) { q.append(tr("Do you wish to continue with the conversion?")); @@ -1526,7 +1520,7 @@ void MdiChild::writeSettings(StatusDialog * status, bool toRadio) // write to T QString radioPath; if (toRadio) { - radioPath = findMassstoragePath("RADIO", true); + radioPath = findMassStoragePath("RADIO", true); qDebug() << "Searching for SD card, found" << radioPath; } else { diff --git a/companion/src/process_flash.cpp b/companion/src/process_flash.cpp deleted file mode 100644 index 31af88a03..000000000 --- a/companion/src/process_flash.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "process_flash.h" -#include "progresswidget.h" -#include "eeprominterface.h" -#include -#include -#include -#include -#include - -#if defined _MSC_VER || !defined __GNUC__ -#include -#include -#include -#define sleep(x) Sleep(x*1000) -#else -#include -#endif - -FlashProcess::FlashProcess(const QString & cmd, const QStringList & args, ProgressWidget * progress): - progress(progress), - cmd(cmd), - args(args), - process(new QProcess(this)), - hasErrors(false), - lfuse(0), - hfuse(0), - efuse(0), - flashPhase(READING) -#if !__GNUC__ - , killTimer(nullptr) -#endif -{ - connect(process, SIGNAL(started()),this, SLOT(onStarted())); - connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput())); - connect(process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError())); - connect(process, SIGNAL(finished(int)), this, SLOT(onFinished(int))); -} - -FlashProcess::~FlashProcess() -{ -#if !__GNUC__ - delete killTimer; -#endif -} - -bool FlashProcess::run() -{ - if (!QFile::exists(cmd)) { - QMessageBox::critical(nullptr, CPN_STR_APP_NAME, tr("Executable %1 not found").arg(cmd)); - return false; - } - -#if !__GNUC__ - if (cmd.toLower().contains("sam-ba")) { - killTimer = new QTimer(this); - connect(killTimer, SIGNAL(timeout()), this, SLOT(onKillTimerElapsed())); - killTimer->start(2000); - } -#endif - - QEventLoop loop; - connect(this, SIGNAL(finished()), &loop, SLOT(quit())); - process->start(cmd, args); - loop.exec(); - - return true; -} - -void FlashProcess::onStarted() -{ - progress->lock(true); - progress->addText(cmd + " " + args.join(" ")); - progress->addSeparator(); -} - -#if !__GNUC__ -bool killProcessByName(const char *szProcessToKill) -{ - QString processToKill{szProcessToKill}; - HANDLE hProcessSnap; - HANDLE hProcess; - PROCESSENTRY32 pe32; - - hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // Takes a snapshot of all the processes - - if (hProcessSnap == INVALID_HANDLE_VALUE) { - return false; - } - - pe32.dwSize = sizeof(PROCESSENTRY32); - - if (!Process32First(hProcessSnap, &pe32)) { - CloseHandle(hProcessSnap); - return false; - } - - do { - QString exeFile{pe32.szExeFile}; - - if (exeFile == processToKill) { // checks if process at current position has the name of to be killed app - hProcess = OpenProcess(PROCESS_TERMINATE,0, pe32.th32ProcessID); // gets handle to process - TerminateProcess(hProcess, 0); // Terminate process by handle - CloseHandle(hProcess); // close the handle - } - } while (Process32Next(hProcessSnap, &pe32)); // gets next member of snapshot - - CloseHandle(hProcessSnap); // closes the snapshot handle - return true; -} -#endif - -void FlashProcess::onKillTimerElapsed() -{ -#if !__GNUC__ - // trick to accelerate SAM-BA startup - killProcessByName("tasklist.exe"); -#endif -} - -void FlashProcess::analyseStandardOutput(const QString & text) -{ - currStdoutLine.append(text); - - if (currStdoutLine.contains("size = ")) { - int pos = currStdoutLine.lastIndexOf("size = "); - QString temp = currStdoutLine.mid(pos+7); - pos = temp.lastIndexOf("\n"); - int size = temp.left(pos).toInt(); - progress->setMaximum(size / 2048); - } - - if (currStdoutLine.contains("\n")) { - int nlPos = currStdoutLine.lastIndexOf("\n"); - currStdoutLine = currStdoutLine.mid(nlPos+1); - } - - if (!currStdoutLine.isEmpty()) { - if (currStdoutLine.at(0) == QChar('.')) { - int pos = currStdoutLine.lastIndexOf("."); - progress->setValue(pos); - } - else if (currStdoutLine.startsWith("Starting upload: [")) { - int pos = (currStdoutLine.lastIndexOf("#") - 19) * 100 / 256; - progress->setValue(pos); - } - } - - if (text.contains("Complete ")) { -#if !__GNUC__ - delete killTimer; - killTimer = nullptr; -#endif - int start = text.indexOf("Complete "); - int end = text.indexOf("%"); - if (start > 0) { - start += 9; - int value = text.mid(start, end - start).toInt(); - progress->setValue(value); - } - } - - if (text.contains("Upload\t[") || text.contains("Download\t[")) { - int end = text.indexOf("%"); - if (end > 0) { - int value = text.mid(end - 3, 3).toInt(); - progress->setValue(value); - } - } - - if (text.contains(":010000")) { - // contains fuse info - QStringList stl = text.split(":01000000"); - foreach (QString t, stl) { - bool ok = false; - if (!lfuse) lfuse = t.left(2).toInt(&ok, 16); - if (!hfuse && !ok) hfuse = t.left(2).toInt(&ok, 16); - if (!efuse && !ok) efuse = t.left(2).toInt(&ok, 16); - } - } - - if (text.contains("-E-")) { - hasErrors = true; - } - - if (text.contains("Cannot open device")) { - hasErrors = true; - } -} - -void FlashProcess::analyseStandardError(const QString &text) -{ - currStderrLine.append(text); - if (currStderrLine.contains("#")) { - QString avrflashPhase = currStderrLine.left(1).toLower(); - if (avrflashPhase == "w") { - flashPhase = WRITING; - progress->setInfo(tr("Writing...")); - progress->setValue(2 * currStderrLine.count("#")); - } - else if (avrflashPhase == "r") { - if (flashPhase == READING) { - progress->setInfo(tr("Reading...")); - } - else { - flashPhase = VERIFYING; - progress->setInfo(tr("Verifying...")); - } - progress->setValue(2 * currStderrLine.count("#")); - } - } - - if (currStderrLine.contains("\n")) { - int nlPos = currStderrLine.lastIndexOf("\n"); - currStderrLine = currStderrLine.mid(nlPos+1); - } - - if ((text.contains("-E-") && !text.contains("-E- No receive file name")) || - text.contains("No DFU capable USB device found")) { - hasErrors = true; - } -} - -void FlashProcess::onReadyReadStandardOutput() -{ - QString text = QString(process->readAllStandardOutput()); - progress->addText(text); - analyseStandardOutput(text); -} - -void FlashProcess::onReadyReadStandardError() -{ - QString text = QString(process->readAllStandardError()); - progress->addText(text); - analyseStandardError(text); -} - -void FlashProcess::errorWizard() -{ - QString output = progress->getText(); - - if (output.contains("avrdude: Expected signature for")) { // wrong signature - int pos = output.indexOf("avrdude: Device signature = "); - bool fwexist = false; - QString DeviceStr = tr("unknown"); - QString FwStr = ""; - - if (pos > 0) { - QString DeviceId = output.mid(pos+28, 8); - if (DeviceId=="0x1e9602") { - DeviceStr = "Atmega 64"; - FwStr="\n" + tr("ie: OpenTX for 9X board or OpenTX for 9XR board"); - fwexist = true; - } - else if (DeviceId=="0x1e9702") { - DeviceStr = "Atmega 128"; - FwStr="\n" + tr("ie: OpenTX for M128 / 9X board or OpenTX for 9XR board with M128 chip"); - fwexist = true; - } - else if (DeviceId=="0x1e9703") { - DeviceStr = "Atmega 1280"; - } - else if (DeviceId=="0x1e9704") { - DeviceStr = "Atmega 1281"; - } - else if (DeviceId=="0x1e9801") { - DeviceStr = "Atmega 2560"; - FwStr="\n" + tr("ie: OpenTX for Gruvin9X board"); - fwexist = true; - } - else if (DeviceId=="0x1e9802") { - DeviceStr = "Atmega 2561"; - } - } - if (!fwexist) { - QMessageBox::warning(nullptr, "Companion - Tip of the day", tr("Your radio uses a %1 CPU!!!\n\nPlease check advanced burn options to set the correct cpu type.").arg(DeviceStr)); - } - else { - Firmware *firmware = getCurrentFirmware(); - QMessageBox::warning(nullptr, "Companion - Tip of the day", tr("Your radio uses a %1 CPU!!!\n\nPlease select an appropriate firmware type to program it.").arg(DeviceStr)+FwStr+tr("\nYou are currently using:\n %1").arg(firmware->getName())); - } - } - else if (output.contains("No DFU capable USB device found")){ - QMessageBox::warning(nullptr, "Companion - Tip of the day", tr("Your radio does not seem connected to USB or the driver is not initialized!!!.")); - } -} - -void FlashProcess::onFinished(int code=0) -{ - progress->addSeparator(); - if (code==1 && cmd.toLower().contains("sam-ba")) { - code = 0; - } - if (code) { - progress->setInfo(tr("Flashing done (exit code = %1)").arg(code)); - if (cmd.toLower().contains("avrdude") || cmd.toLower().contains("dfu")) { - errorWizard(); - } - } - else if (hasErrors) { - progress->setInfo(tr("Flashing done with errors")); - } - if (lfuse || hfuse || efuse) { - addReadFuses(); - } - progress->setValue(progress->maximum()); - progress->lock(false); - emit finished(); -} - -void FlashProcess::addReadFuses() -{ - progress->addSeparator(); - progress->addText(tr("FUSES: Low=%1 High=%2 Ext=%3").arg(lfuse, 2, 16, QChar('0')).arg(hfuse, 2, 16, QChar('0')).arg(efuse, 2, 16, QChar('0'))); - progress->addSeparator(); -} diff --git a/companion/src/process_flash.h b/companion/src/process_flash.h deleted file mode 100644 index 24ea88c4e..000000000 --- a/companion/src/process_flash.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _PROCESS_FLASH_H_ -#define _PROCESS_FLASH_H_ - -#include -#include -#include - -class ProgressWidget; -class QProcess; -class QTimer; - -class FlashProcess : public QObject -{ - Q_OBJECT - -public: - FlashProcess(const QString &cmd, const QStringList &args, ProgressWidget *progress); - ~FlashProcess(); - bool run(); - -signals: - void finished(); - -protected slots: - void onStarted(); - void onReadyReadStandardOutput(); - void onReadyReadStandardError(); - void onFinished(int); - void onKillTimerElapsed(); - -protected: - void analyseStandardOutput(const QString &text); - void analyseStandardError(const QString &text); - void errorWizard(); - void addReadFuses(); - ProgressWidget * progress; - const QString cmd; - const QStringList args; - QProcess * process; - bool hasErrors; - QString currStdoutLine; - QString currStderrLine; - unsigned int lfuse; - unsigned int hfuse; - unsigned int efuse; - enum FlashPhase { READING, WRITING, VERIFYING }; - FlashPhase flashPhase; -#if !__GNUC__ - QTimer *killTimer; -#endif -}; - -#endif // _PROCESS_FLASH_H_ diff --git a/companion/src/progress/CMakeLists.txt b/companion/src/progress/CMakeLists.txt new file mode 100644 index 000000000..d423147e7 --- /dev/null +++ b/companion/src/progress/CMakeLists.txt @@ -0,0 +1,30 @@ + +project(progress) + +set(${PROJECT_NAME}_NAMES + progressdialog + progresswidget +) + +AddHeadersSources() + +qt_wrap_cpp(${PROJECT_NAME}_SRCS +) + +add_library(${PROJECT_NAME} + ${${PROJECT_NAME}_HDRS} + ${${PROJECT_NAME}_SRCS} +) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + ${CPN_COMMON_LIB} +) + +target_include_directories(${PROJECT_NAME} + PRIVATE + "${CMAKE_SOURCE_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}" + PUBLIC + "${CMAKE_CURRENT_LIST_DIR}" +) diff --git a/companion/src/progressdialog.cpp b/companion/src/progress/progressdialog.cpp similarity index 89% rename from companion/src/progressdialog.cpp rename to companion/src/progress/progressdialog.cpp index 02b5b4e94..25da343fe 100644 --- a/companion/src/progressdialog.cpp +++ b/companion/src/progress/progressdialog.cpp @@ -26,10 +26,10 @@ #include ProgressDialog::ProgressDialog(QWidget *parent, const QString &title, const QIcon &icon, bool forceOpen): -QDialog(parent), -ui(new Ui::ProgressDialog), -locked(false), -keepOpen(false) + QDialog(parent), + ui(new Ui::ProgressDialog), + locked(false), + keepOpen(false) { ui->setupUi(this); setWindowTitle(title); @@ -39,6 +39,7 @@ keepOpen(false) ui->outputProgress->forceOpen(); } resize(0, 0); + connect(ui->outputProgress, &ProgressWidget::locked, this, &ProgressDialog::on_outputProgress_locked); show(); } @@ -64,7 +65,7 @@ void ProgressDialog::on_closeButton_clicked() void ProgressDialog::on_outputProgress_detailsToggled() { - QTimer::singleShot(0, this, SLOT(shrink())); + QTimer::singleShot(0, [=]() { adjustSize(); }); } void ProgressDialog::on_outputProgress_locked(bool lock) @@ -73,11 +74,6 @@ void ProgressDialog::on_outputProgress_locked(bool lock) locked = lock; } -void ProgressDialog::shrink() -{ - resize(0, 0); -} - bool ProgressDialog::isEmpty() const { return ui->outputProgress->isEmpty(); diff --git a/companion/src/progressdialog.h b/companion/src/progress/progressdialog.h similarity index 98% rename from companion/src/progressdialog.h rename to companion/src/progress/progressdialog.h index 5633a575d..5bbb77d7c 100644 --- a/companion/src/progressdialog.h +++ b/companion/src/progress/progressdialog.h @@ -50,7 +50,6 @@ class ProgressDialog : public QDialog void on_outputProgress_detailsToggled(); void on_outputProgress_locked(bool); void on_outputProgress_keepOpen(bool); - void shrink(); private: Ui::ProgressDialog *ui; diff --git a/companion/src/progressdialog.ui b/companion/src/progress/progressdialog.ui similarity index 100% rename from companion/src/progressdialog.ui rename to companion/src/progress/progressdialog.ui diff --git a/companion/src/progresswidget.cpp b/companion/src/progress/progresswidget.cpp similarity index 99% rename from companion/src/progresswidget.cpp rename to companion/src/progress/progresswidget.cpp index ecbc6892d..e63b260f0 100644 --- a/companion/src/progresswidget.cpp +++ b/companion/src/progress/progresswidget.cpp @@ -52,7 +52,6 @@ ProgressWidget::ProgressWidget(QWidget *parent) : newFont.setPointSize(9); #endif ui->textEdit->setFont(newFont); - } ProgressWidget::~ProgressWidget() diff --git a/companion/src/progresswidget.h b/companion/src/progress/progresswidget.h similarity index 100% rename from companion/src/progresswidget.h rename to companion/src/progress/progresswidget.h diff --git a/companion/src/progresswidget.ui b/companion/src/progress/progresswidget.ui similarity index 100% rename from companion/src/progresswidget.ui rename to companion/src/progress/progresswidget.ui diff --git a/companion/src/radiointerface.cpp b/companion/src/radiointerface.cpp deleted file mode 100644 index f6026cabc..000000000 --- a/companion/src/radiointerface.cpp +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "radiointerface.h" -#include "appdata.h" -#include "eeprominterface.h" -#include "process_flash.h" -#include "radionotfound.h" -#include "burnconfigdialog.h" -#include "helpers.h" -#include "process_copy.h" -#include "storage.h" -#include "progresswidget.h" - -QString getRadioInterfaceCmd() -{ - burnConfigDialog bcd; - Board::Type board = getCurrentBoard(); - if (IS_STM32(board)) { - return bcd.getDFU(); - } - else - return bcd.getSAMBA(); -} - -QStringList getDfuArgs(const QString & cmd, const QString & filename) -{ - QStringList args; - burnConfigDialog bcd; - args << bcd.getDFUArgs(); - if (!filename.endsWith(".dfu")) - args << "--dfuse-address" << "0x08000000"; - if (cmd == "-U") - args.last().append(":" % QString::number(Boards::getFlashSize(getCurrentBoard()))); - args << "--device" << "0483:df11"; - args << cmd % filename; - return args; -} - -QStringList getSambaArgs(const QString & tcl) -{ - QStringList result; - - QString tclFilename = generateProcessUniqueTempFileName("temp.tcl"); - if (QFile::exists(tclFilename)) { - qunlink(tclFilename); - } - QFile tclFile(tclFilename); - if (!tclFile.open(QIODevice::WriteOnly | QIODevice::Text)) { - QMessageBox::warning(NULL, CPN_STR_TTL_ERROR, - QCoreApplication::translate("RadioInterface", "Cannot write file %1:\n%2.").arg(tclFilename).arg(tclFile.errorString())); - return result; - } - - QTextStream outputStream(&tclFile); - outputStream << tcl; - - burnConfigDialog bcd; - result << bcd.getSambaPort() << bcd.getArmMCU() << tclFilename ; - return result; -} - -QStringList getReadEEpromCmd(const QString & filename) -{ - QStringList result; - if (!IS_STM32(getCurrentBoard())) { - result = getSambaArgs(QString("SERIALFLASH::Init 0\n") + "receive_file {SerialFlash AT25} \"" + filename + "\" 0x0 0x80000 0\n"); - } - return result; -} - -QStringList getWriteEEpromCmd(const QString & filename) -{ - Board::Type board = getCurrentBoard(); - if (IS_STM32(board)) { - // impossible - return QStringList(); - } - else { - return getSambaArgs(QString("SERIALFLASH::Init 0\n") + "send_file {SerialFlash AT25} \"" + filename + "\" 0x0 0\n"); - } -} - -QStringList getWriteFirmwareArgs(const QString & filename) -{ - Board::Type board = getCurrentBoard(); - if (IS_STM32(board)) { - return getDfuArgs("-D", filename); - } - else { - return getSambaArgs(QString("send_file {Flash} \"") + filename + "\" 0x400000 0\n" + "FLASH::ScriptGPNMV 2\n"); - } -} - -QStringList getReadFirmwareArgs(const QString & filename) -{ - Board::Type board = getCurrentBoard(); - if (IS_STM32(board)) { - return getDfuArgs("-U", filename); - } - else { - return getSambaArgs(QString("receive_file {Flash} \"") + filename + "\" 0x400000 0x80000 0\n"); - } -} - -bool readFirmware(const QString & filename, ProgressWidget * progress) -{ - bool result = false; - - QFile file(filename); - if (file.exists() && !file.remove()) { - QMessageBox::warning(NULL, CPN_STR_TTL_ERROR, - QCoreApplication::translate("RadioInterface", "Could not delete temporary file: %1").arg(filename)); - return false; - } - - QString path = findMassstoragePath("FIRMWARE.BIN"); - if (!path.isEmpty()) { - qDebug() << "readFirmware: reading" << path << "into" << filename; - CopyProcess copyProcess(path, filename, progress); - result = copyProcess.run(); - } - - if (!result) { - qDebug() << "readFirmware: reading" << filename << "with" << getRadioInterfaceCmd() << getReadFirmwareArgs(filename); - FlashProcess flashProcess(getRadioInterfaceCmd(), getReadFirmwareArgs(filename), progress); - result = flashProcess.run(); - } - - if (!QFileInfo(filename).exists()) { - result = false; - } - - return result; -} - -bool writeFirmware(const QString & filename, ProgressWidget * progress) -{ - QString path = findMassstoragePath("FIRMWARE.BIN"); - if (!path.isEmpty()) { - qDebug() << "writeFirmware: writing" << path << "from" << filename; - CopyProcess copyProcess(filename, path, progress); - return copyProcess.run(); - } - - qDebug() << "writeFirmware: writing" << filename << "with" << getRadioInterfaceCmd() << getWriteFirmwareArgs(filename); - FlashProcess flashProcess(getRadioInterfaceCmd(), getWriteFirmwareArgs(filename), progress); - return flashProcess.run(); -} - -bool readSettings(const QString & filename, ProgressWidget * progress) -{ - Board::Type board = getCurrentBoard(); - - QFile file(filename); - if (file.exists() && !file.remove()) { - QMessageBox::warning(NULL, CPN_STR_TTL_ERROR, - QCoreApplication::translate("RadioInterface", "Could not delete temporary file: %1").arg(filename)); - return false; - } - - if (Boards::getCapability(board, Board::HasSDCard)) - return readSettingsSDCard(filename, progress); - else - return readSettingsEeprom(filename, progress); -} - -bool readSettingsSDCard(const QString & filename, ProgressWidget * progress, bool fromRadio) -{ - QString radioPath; - - if (fromRadio) { - radioPath = findMassstoragePath("RADIO", true, progress); - qDebug() << "Searching for SD card, found" << radioPath; - } - else { - radioPath = g.currentProfile().sdPath(); - if (!QFile::exists(radioPath % "/RADIO")) - radioPath.clear(); - } - - if (radioPath.isEmpty()) { - QMessageBox::critical(progress, CPN_STR_TTL_ERROR, - QCoreApplication::translate("RadioInterface", "Unable to find SD card!")); - return false; - } - - RadioData radioData; - Storage inputStorage(radioPath); - if (!inputStorage.load(radioData)) { - QString errorMsg = inputStorage.error(); - if (errorMsg.isEmpty()) { - errorMsg = QCoreApplication::translate("RadioInterface", "Failed to read Models and Settings from") % radioPath; - } - QMessageBox::critical(progress, CPN_STR_TTL_ERROR, errorMsg); - return false; - } - Storage outputStorage(filename); - if (!outputStorage.write(radioData)) { - QMessageBox::critical(progress, CPN_STR_TTL_ERROR, QCoreApplication::translate("RadioInterface", "Failed to write Models and Setting file") % " " % filename); - return false; - } - - if (getCurrentBoard() == Board::BOARD_JUMPER_T16 && inputStorage.getBoard() == Board::BOARD_X10) { - if (displayT16ImportWarning() == false) - return false; - } - - return QFileInfo(filename).exists(); -} - - -bool readSettingsEeprom(const QString & filename, ProgressWidget * progress) -{ - Board::Type board = getCurrentBoard(); - - QString path = findMassstoragePath("EEPROM.BIN"); - if (path.isEmpty()) { - // On previous OpenTX we called the EEPROM file "TARANIS.BIN" :( - path = findMassstoragePath("TARANIS.BIN"); - } - if (path.isEmpty()) { - // Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :( - path = findMassstoragePath("ERSKY9X.BIN"); - } - if (path.isEmpty()) { - RadioNotFoundDialog dialog(progress); - dialog.exec(); - return false; - } - CopyProcess copyProcess(path, filename, progress); - if (!copyProcess.run()) { - return false; - } - - - if (!IS_STM32(board)) { - FlashProcess flashProcess(getRadioInterfaceCmd(), getReadEEpromCmd(filename), progress); - if (!flashProcess.run()) { - return false; - } - } - - return QFileInfo(filename).exists(); -} - -bool writeSettings(const QString & filename, ProgressWidget * progress) -{ - Board::Type board = getCurrentBoard(); - - QString path = findMassstoragePath("EEPROM.BIN"); - if (path.isEmpty()) { - // On previous OpenTX we called the EEPROM file "TARANIS.BIN" :( - path = findMassstoragePath("TARANIS.BIN"); - } - if (path.isEmpty()) { - // Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :( - path = findMassstoragePath("ERSKY9X.BIN"); - } - if (!path.isEmpty()) { - CopyProcess copyProcess(filename, path, progress); - return copyProcess.run(); - } - - if (!IS_TARANIS(board)) { - FlashProcess flashProcess(getRadioInterfaceCmd(), getWriteEEpromCmd(filename), progress); - return flashProcess.run(); - } - - RadioNotFoundDialog dialog(progress); - dialog.exec(); - - return false; -} - -QString findMassstoragePath(const QString & filename, bool onlyPath, ProgressWidget *progress) -{ - QString foundPath; - QString foundProbefile; - int found = 0; - - QRegularExpression fstypeRe("^(v?fat|msdos|lifs)", QRegularExpression::CaseInsensitiveOption); // Linux: "vfat"; macOS: "msdos" or "lifs"; Win: "FAT32" - - foreach(const QStorageInfo & si, QStorageInfo::mountedVolumes()) { - //qDebug() << si.rootPath() << si.name() << si.device() << si.displayName() << si.fileSystemType() << si.isReady() << si.bytesTotal() << si.blockSize(); - if (!si.isReady() || si.isReadOnly() || !QString(si.fileSystemType()).contains(fstypeRe)) - continue; - - QString temppath = si.rootPath(); - QString probefile = temppath % "/" % filename; - qDebug() << "Searching for" << probefile; - - if (QFile::exists(probefile)) { - found++; - foundPath = temppath; - foundProbefile = probefile; - qDebug() << probefile << "found"; - } - } - - if (found == 1) - return onlyPath ? foundPath : foundProbefile; - else if (found > 1) { - QMessageBox::critical(progress, CPN_STR_TTL_ERROR, filename % " " % QCoreApplication::translate("RadioInterface", "found in multiple locations")); - } - - return QString(); -} diff --git a/companion/src/radiointerface.h b/companion/src/radiointerface.h deleted file mode 100644 index 4b930df9c..000000000 --- a/companion/src/radiointerface.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#pragma once - -#include -#include - -class ProgressWidget; - -QString getRadioInterfaceCmd(); - -QString findMassstoragePath(const QString &filename, bool onlyPath = false, ProgressWidget *progress = nullptr); - -QStringList getSambaArgs(const QString &tcl); -QStringList getDfuArgs(const QString &cmd, const QString &filename); - -QStringList getReadEEpromCmd(const QString &filename); -QStringList getWriteEEpromCmd(const QString &filename); -QStringList getReadFirmwareArgs(const QString &filename); -QStringList getWriteFirmwareArgs(const QString &filename); - -bool readFirmware(const QString &filename, ProgressWidget *progress); -bool writeFirmware(const QString &filename, ProgressWidget *progress); -bool readSettings(const QString &filename, ProgressWidget *progress); -bool readSettingsSDCard(const QString &filename, ProgressWidget *progress, bool fromRadio = true); -bool readSettingsEeprom(const QString &filename, ProgressWidget *progress); -bool writeSettings(const QString &filename, ProgressWidget *progress); diff --git a/companion/src/radionotfound.cpp b/companion/src/radionotfound.cpp deleted file mode 100644 index 3ad0ad4cc..000000000 --- a/companion/src/radionotfound.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "radionotfound.h" -#include "ui_radionotfound.h" - -RadioNotFoundDialog::RadioNotFoundDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::RadioNotFoundDialog) -{ - ui->setupUi(this); -} - -RadioNotFoundDialog::~RadioNotFoundDialog() -{ - delete ui; -} - -void RadioNotFoundDialog::on_OK_clicked() -{ - accept(); -} diff --git a/companion/src/radionotfound.h b/companion/src/radionotfound.h deleted file mode 100644 index fb0042f34..000000000 --- a/companion/src/radionotfound.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _RADIONOTFOUND_H_ -#define _RADIONOTFOUND_H_ - -#include - -namespace Ui { - class RadioNotFoundDialog; -} - -class RadioNotFoundDialog : public QDialog -{ - Q_OBJECT -public: - explicit RadioNotFoundDialog(QWidget *parent = 0); - ~RadioNotFoundDialog(); -private: - Ui::RadioNotFoundDialog *ui; -private slots: - void on_OK_clicked(); -}; - -#endif // _RADIONOTFOUND_H_ diff --git a/companion/src/radionotfound.ui b/companion/src/radionotfound.ui deleted file mode 100644 index 5282ad743..000000000 --- a/companion/src/radionotfound.ui +++ /dev/null @@ -1,117 +0,0 @@ - - - RadioNotFoundDialog - - - - 0 - 0 - 367 - 350 - - - - - 0 - 0 - - - - - 367 - 350 - - - - - 367 - 350 - - - - No Radio Found - - - - - - - - <html><head/><body><p>No Radio was found!</p><p>Make sure that you hold the lower trim buttons towards the center while you turn it on.</p><p>Then connect the USB wire.</p><p><span style=" font-family:'arial,sans-serif'; font-size:13px; font-style:italic; color:#222222; background-color:#ffffff;">Note: if you have a Taranis that has not had the firmware upgraded to 2.0 then this version of Companion will not work.</span></p></body></html> - - - Qt::RichText - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - 0 - 0 - - - - QFrame::Box - - - - - - :/images/taranison.png - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - OK - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - diff --git a/companion/src/splash.h b/companion/src/splash.h deleted file mode 100644 index b2a0bfe02..000000000 --- a/companion/src/splash.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -const unsigned char gr9x_splash[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x08, 0x04, 0x04, 0x84, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xc4, 0x04, 0x04, 0x88, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0xfc, 0x04, 0x04, 0x00, 0xe0, 0x50, 0x50, 0x60, 0x00, 0xfe, 0x00, 0xe0, 0x50, 0x50, 0x60, 0x00, 0xf0, 0x10, 0x10, 0xe0, 0x10, 0x10, 0xe0, 0x00, 0xe0, 0x50, 0x50, 0x60, 0x00, 0xfc, 0x10, 0x00, 0xf0, 0x20, 0x10, 0x00, 0x10, 0xe0, 0x00, 0xe0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x24, 0xc4, 0x00, 0xe0, 0x50, 0x50, 0x60, 0x00, 0xf0, 0x20, 0x10, 0x00, 0xf4, 0x00, 0xe0, 0x50, 0x50, 0x60, 0x00, 0x30, 0x50, 0x90, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xbf, 0x80, 0x80, 0x80, 0xbf, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x30, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x88, 0x09, 0x09, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x81, 0xf0, 0x18, 0x19, 0xf0, 0x80, 0x01, 0x00, 0x00, 0x01, 0x01, 0x21, 0x20, 0x20, 0x21, 0x20, 0x21, 0x20, 0x20, 0x20, 0x20, 0x24, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0xe1, 0x21, 0x21, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x20, 0x21, 0x20, 0x20, 0x20, 0x21, 0x20, 0x20, 0x21, 0x21, 0x21, 0x20, 0x81, 0xc1, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xf8, 0xfe, 0xff, 0xff, 0xff, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x37, 0x47, 0x87, 0x87, 0x87, 0x97, 0x97, 0x07, 0x5f, 0xdf, 0xdf, 0xdf, 0xde, 0xd8, 0x80, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x00, 0x80, 0x40, 0x3f, 0x80, 0xc0, 0xe0, 0xe0, 0xe0, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x58, 0x48, 0x4c, 0x4c, 0x47, 0x47, 0x44, 0x46, 0x42, 0x43, 0xff, 0x00, 0x00, 0xff, 0x43, 0x42, 0x46, 0x44, 0x47, 0x47, 0x4c, 0x4c, 0x48, 0x58, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xb0, 0x18, 0x0c, 0x04, 0x06, 0x87, 0x8f, 0xbf, 0xee, 0xfe, 0xf6, 0x7e, 0x3c, 0x1c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0e, 0x1f, 0x39, 0x60, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x8e, 0xce, 0xee, 0xfc, 0xf9, 0x73, 0x27, 0x0f, 0x1f, 0x3f, 0x7e, 0xfc, 0xf9, 0xf1, 0xf1, 0xf9, 0xfc, 0x3e, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0x7f, 0x00, 0x00, 0x7f, 0x40, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x40, 0xc0, 0xc0, 0x80, 0x88, 0x10, 0x10, 0x1d, 0x17, 0x11, 0x11, 0x13, 0x13, 0x13, 0x13, 0x11, 0x17, 0x1d, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1e, 0x1e, 0x1e, 0x0e, 0x0e, 0x0f, 0x07, 0x07, 0x03, 0x03, 0x01, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0x7c, 0x3e, 0x1f, 0x0f, 0x07, 0x07, 0x0f, 0x1f, 0x3f, 0x7e, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0xe0, 0x20, 0x20, 0x00, 0xe0, 0x00, 0x80, 0x40, 0x20, 0x00, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x02, 0x02, 0xc1, 0x40, 0x40, 0xfc, 0x3f, 0x7b, 0xb1, 0xe0, 0xae, 0xa8, 0xea, 0xee, 0xa1, 0x7b, 0x7f, 0xfc, 0x40, 0x40, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xf8, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0e, 0x0f, 0x0f, 0x0f, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x0f, 0x0f, 0x0e, 0x0c, 0x00, 0x08, 0x09, 0x09, 0x09, 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x02, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x38, 0x38, 0xff, 0x95, 0x80, 0xb2, 0x82, 0xfa, 0xaa, 0xfa, 0x82, 0xf2, 0x90, 0x95, 0xff, 0x38, 0x38, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0xb0, 0x74, 0xf6, 0x77, 0x77, 0xf7, 0x77, 0xf7, 0x77, 0xf7, 0x77, 0xf7, 0x74, 0xf6, 0x77, 0xf7, 0x77, 0xf6, 0x34, 0xb0, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x28, 0x2c, 0x24, 0x2f, 0x3f, 0x36, 0x07, 0x36, 0x1f, 0x0e, 0x1f, 0x36, 0x07, 0x36, 0x3f, 0x2f, 0x24, 0x2c, 0x28, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xc1, 0xc0, 0x41, 0x41, 0x40, 0xc1, 0xc0, 0x41, 0x40, 0x41, 0xc0, 0xc1, 0x40, 0xc1, 0xc1, 0xc1, 0xc0, 0x41, 0xc0, 0xc1, 0x00, 0x00, - 0x00, 0x00, 0x7f, 0x11, 0x11, 0x0e, 0x00, 0x0e, 0x11, 0x11, 0x0e, 0x00, 0x07, 0x18, 0x0c, 0x03, 0x0e, 0x18, 0x07, 0x00, 0x0e, 0x15, 0x15, 0x16, 0x00, 0x1f, 0x02, 0x01, 0x0e, 0x15, 0x15, 0x16, 0x00, 0x0e, 0x11, 0x11, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x11, 0x11, 0x0e, 0x00, 0x01, 0x4e, 0x30, 0x0e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x11, 0x11, 0x0e, 0x00, 0x7f, 0x11, 0x11, 0x0e, 0x00, 0x0e, 0x15, 0x15, 0x16, 0x00, 0x1f, 0x01, 0x01, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x13, 0x15, 0x19, 0x00, 0x0e, 0x11, 0x11, 0x0e, 0x00, 0x0f, 0x10, 0x10, 0x1f, 0x00, 0x1f, 0x02, 0x01, 0x0e, 0x11, 0x11, 0x00, 0x0e, 0x15, 0x15, 0x16, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x2f, 0x2f, 0x2b, 0x32, 0x3f, 0x20, 0x3b, 0x3b, 0x3c, 0x3f, 0x20, 0x2f, 0x2f, 0x3f, 0x26, 0x2b, 0x2c, 0x3f, 0x00, 0x00, -}; - -const unsigned char gr9xv4_splash[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x08, 0x04, 0x04, 0x84, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0xc4, 0x04, 0x04, 0x88, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0xfc, 0x04, 0x04, 0x00, 0xe0, 0x50, 0x50, 0x60, 0x00, 0xfe, 0x00, 0xe0, 0x50, 0x50, 0x60, 0x00, 0xf0, 0x10, 0x10, 0xe0, 0x10, 0x10, 0xe0, 0x00, 0xe0, 0x50, 0x50, 0x60, 0x00, 0xfc, 0x10, 0x00, 0xf0, 0x20, 0x10, 0x00, 0x10, 0xe0, 0x00, 0xe0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x24, 0xc4, 0x00, 0xe0, 0x50, 0x50, 0x60, 0x00, 0xf0, 0x20, 0x10, 0x00, 0xf4, 0x00, 0xe0, 0x50, 0x50, 0x60, 0x00, 0x30, 0x50, 0x90, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xbf, 0x80, 0x80, 0x80, 0xbf, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x30, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x88, 0x09, 0x09, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x81, 0xf0, 0x18, 0x19, 0xf0, 0x80, 0x01, 0x00, 0x00, 0x01, 0x01, 0x21, 0x20, 0x20, 0x21, 0x20, 0x21, 0x20, 0x20, 0x20, 0x20, 0x24, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0xe1, 0x21, 0x21, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x20, 0x21, 0x20, 0x20, 0x20, 0x21, 0x20, 0x20, 0x21, 0x21, 0x21, 0x20, 0x81, 0xc1, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xf8, 0xfe, 0xff, 0xff, 0xff, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x37, 0x47, 0x87, 0x87, 0x87, 0x97, 0x97, 0x07, 0x5f, 0xdf, 0xdf, 0xdf, 0xde, 0xd8, 0x80, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x00, 0x80, 0x40, 0x3f, 0x80, 0xc0, 0xe0, 0xe0, 0xe0, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x58, 0x48, 0x4c, 0x4c, 0x47, 0x47, 0x44, 0x46, 0x42, 0x43, 0xff, 0x00, 0x00, 0xff, 0x43, 0x42, 0x46, 0x44, 0x47, 0x47, 0x4c, 0x4c, 0x48, 0x58, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xb0, 0x18, 0x0c, 0x04, 0x06, 0x87, 0x8f, 0xbf, 0xee, 0xfe, 0xf6, 0x7e, 0x3c, 0x1c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0e, 0x1f, 0x39, 0x60, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x8e, 0xce, 0xee, 0xfc, 0xf9, 0x73, 0x27, 0x0f, 0x1f, 0x3f, 0x7e, 0xfc, 0xf9, 0xf1, 0xf1, 0xf9, 0xfc, 0x3e, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0x7f, 0x00, 0x00, 0x7f, 0x40, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x40, 0xc0, 0xc0, 0x80, 0x88, 0x10, 0x10, 0x1d, 0x17, 0x11, 0x11, 0x13, 0x13, 0x13, 0x13, 0x11, 0x17, 0x1d, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1e, 0x1e, 0x1e, 0x0e, 0x0e, 0x0f, 0x07, 0x07, 0x03, 0x03, 0x01, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0x7c, 0x3e, 0x1f, 0x0f, 0x07, 0x07, 0x0f, 0x1f, 0x3f, 0x7e, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x7c, 0xf8, 0x80, 0x80, 0xf8, 0x7c, 0x0c, 0x00, 0xe0, 0xf0, 0xdc, 0xce, 0xc3, 0xff, 0xff, 0xc0, 0xc0, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x02, 0x02, 0xc1, 0x40, 0x40, 0xfc, 0x3f, 0x7b, 0xb1, 0xe0, 0xae, 0xa8, 0xea, 0xee, 0xa1, 0x7b, 0x7f, 0xfc, 0x40, 0x40, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xf8, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0e, 0x0f, 0x0f, 0x0f, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x0f, 0x0f, 0x0e, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x0f, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x38, 0x38, 0xff, 0x95, 0x80, 0xb2, 0x82, 0xfa, 0xaa, 0xfa, 0x82, 0xf2, 0x90, 0x95, 0xff, 0x38, 0x38, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0xb0, 0x74, 0xf6, 0x77, 0x77, 0xf7, 0x77, 0xf7, 0x77, 0xf7, 0x77, 0xf7, 0x74, 0xf6, 0x77, 0xf7, 0x77, 0xf6, 0x34, 0xb0, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x28, 0x2c, 0x24, 0x2f, 0x3f, 0x36, 0x07, 0x36, 0x1f, 0x0e, 0x1f, 0x36, 0x07, 0x36, 0x3f, 0x2f, 0x24, 0x2c, 0x28, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xc1, 0xc0, 0x41, 0x41, 0x40, 0xc1, 0xc0, 0x41, 0x40, 0x41, 0xc0, 0xc1, 0x40, 0xc1, 0xc1, 0xc1, 0xc0, 0x41, 0xc0, 0xc1, 0x00, 0x00, - 0x00, 0x00, 0x7f, 0x11, 0x11, 0x0e, 0x00, 0x0e, 0x11, 0x11, 0x0e, 0x00, 0x07, 0x18, 0x0c, 0x03, 0x0e, 0x18, 0x07, 0x00, 0x0e, 0x15, 0x15, 0x16, 0x00, 0x1f, 0x02, 0x01, 0x0e, 0x15, 0x15, 0x16, 0x00, 0x0e, 0x11, 0x11, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x11, 0x11, 0x0e, 0x00, 0x01, 0x4e, 0x30, 0x0e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x11, 0x11, 0x0e, 0x00, 0x7f, 0x11, 0x11, 0x0e, 0x00, 0x0e, 0x15, 0x15, 0x16, 0x00, 0x1f, 0x01, 0x01, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x13, 0x15, 0x19, 0x00, 0x0e, 0x11, 0x11, 0x0e, 0x00, 0x0f, 0x10, 0x10, 0x1f, 0x00, 0x1f, 0x02, 0x01, 0x0e, 0x11, 0x11, 0x00, 0x0e, 0x15, 0x15, 0x16, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x2f, 0x2f, 0x2b, 0x32, 0x3f, 0x20, 0x3b, 0x3b, 0x3c, 0x3f, 0x20, 0x2f, 0x2f, 0x3f, 0x26, 0x2b, 0x2c, 0x3f, 0x00, 0x00, -}; - -const unsigned char er9x_splash[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0xf8, 0xf8, 0xfc, 0xfc, 0xfc, 0xf8, 0xf8, 0xf8, 0xf0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xc0, 0xe0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xe0, 0xc0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf8, 0xff, 0xff, 0x9f, 0x87, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x03, 0x83, 0x80, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0xc3, 0xe3, 0x73, 0x3f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x3f, 0x1f, 0x1f, 0x0f, 0x07, 0x83, 0xc3, 0xe1, 0xe1, 0xe0, 0xe0, 0xc0, 0x00, 0x0e, 0x1f, 0x3f, 0x7f, 0x7f, 0xff, 0xf3, 0xe0, 0xe0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xf0, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x3f, 0x0f, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x80, 0xf0, 0xfe, 0xff, 0xff, 0xc7, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0x01, 0xc1, 0xf0, 0xfe, 0xff, 0x3f, 0x0f, 0x07, 0x0f, 0x0d, 0x0d, 0x0d, 0x1d, 0x19, 0x19, 0x19, 0x19, 0x31, 0x31, 0x31, 0x31, 0x71, 0x61, 0x61, 0x61, 0xe1, 0xc1, 0xc1, 0xc1, 0x81, 0x00, 0x00, 0x00, 0x80, 0xe0, 0xf8, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x2f, 0x17, 0x0f, 0x03, 0x81, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x07, 0x07, 0x0e, 0x1e, 0x1c, 0x38, 0x70, 0xe0, 0xf0, 0xf8, 0xfe, 0xff, 0xff, 0x7f, 0x1f, 0x0f, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf8, 0xfc, 0xfc, 0xfe, 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xe0, 0xf8, 0xfe, 0xff, 0xff, 0x7f, 0x1f, 0x0f, 0x1f, 0x3d, 0x7c, 0xf8, 0xf8, 0xf0, 0xe0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, 0x07, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0xe0, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf0, 0x78, 0xde, 0x27, 0x13, 0x09, 0x0e, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x0f, 0x7f, 0xff, 0xfc, 0x78, 0x78, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xf0, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xe0, 0x38, 0x1e, 0x0f, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x0e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x00, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0f, 0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf8, 0xbf, 0xff, 0xff, 0xff, 0x1f, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xfc, 0xff, 0x7f, 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x1c, 0x07, 0x0e, 0x0f, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -const unsigned char opentx_splash[] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x08,0x04,0x04,0x84,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0xc4,0x04,0x04,0x88,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xbf,0x80,0x80,0x80,0xbf,0x80,0x80,0x80,0x80,0x00,0x00,0x30,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x88,0x09,0x09,0x08,0xf0,0x00,0x00,0x00,0x01,0x01,0x01,0x7f,0x01,0x01,0x01,0x01,0x00,0x00,0x7f,0x49,0x49,0x49,0x49,0x41,0x41,0x40,0x00,0x7f,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x7f,0x49,0x49,0x49,0x49,0x41,0x41,0x40,0x00,0x7f,0x01,0x06,0x0c,0x10,0x10,0x0c,0x06,0x01,0x7f,0x00,0x00,0x7f,0x49,0x49,0x49,0x49,0x41,0x41,0x40,0x01,0x01,0x01,0x7f,0x01,0x01,0x01,0x01,0x00,0x00,0x7f,0x09,0x09,0x09,0x09,0x19,0x29,0x4e,0x00,0x01,0x03,0x06,0x0c,0x78,0x0c,0x06,0x03,0x01,0x00,0x00, - 0x00,0x00,0xf8,0xfe,0xff,0xff,0xff,0x0f,0x07,0x07,0x07,0x07,0x07,0x07,0x37,0x47,0x87,0x87,0x87,0x97,0x97,0x07,0x5f,0xdf,0xdf,0xdf,0xde,0xd8,0x80,0x10,0x10,0x10,0x10,0x10,0x0f,0x00,0x80,0x40,0x3f,0x80,0xc0,0xe0,0xe0,0xe0,0x60,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xd0,0x90,0x90,0x90,0x90,0x90,0x00,0x00,0x00,0xf0,0x90,0x90,0x90,0x90,0x10,0x10,0x00,0x00,0xf0,0x90,0x90,0x90,0x90,0x90,0x90,0xe0,0x00,0x00,0xf0,0x00,0x00,0x00,0xf0,0x90,0x90,0x90,0x90,0x10,0x10,0x00,0x60,0xd0,0x90,0x90,0x90,0x90,0x90,0x00,0x00,0x00,0x00, - 0x00,0x00,0x01,0x07,0x07,0x0f,0x0f,0x0f,0x0e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x0e,0x0e,0x0e,0x0e,0x0e,0x8e,0xce,0xee,0xfc,0xf9,0x73,0x27,0x0f,0x1f,0x3f,0x7e,0xfc,0xf9,0xf1,0xf1,0xf9,0xfc,0x3e,0x1f,0x0f,0x07,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x04,0x04,0x04,0x04,0x04,0x03,0x00,0x00,0x07,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,0x07,0x00,0x00,0x00,0x00,0x01,0x02,0x04,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x02,0x06,0x04,0x04,0x04,0x04,0x04,0x03,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1e,0x1e,0x1e,0x0e,0x0e,0x0f,0x07,0x07,0x03,0x03,0x01,0x80,0xc0,0xe0,0xf0,0xf8,0x7c,0x3e,0x1f,0x0f,0x07,0x07,0x0f,0x1f,0x3f,0x7e,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x4a,0x4a,0x4a,0x4a,0x4a,0x7c,0x00,0x02,0x06,0x0c,0x70,0x08,0x04,0x02,0x02,0x00,0x00,0x00,0x00,0x1c,0x22,0x41,0x41,0x49,0x49,0x49,0x3a,0x18,0x00,0x00,0x7e,0x12,0x12,0x12,0x12,0x32,0x4c,0x00,0x00,0x3e,0x40,0x40,0x40,0x40,0x40,0x3e,0x00,0x02,0x0c,0x18,0x60,0x60,0x30,0x0c,0x02,0x00,0x7e,0x00,0x00,0x7e,0x02,0x0c,0x18,0x30,0x40,0x7e,0x00,0x00,0x00,0x00, - 0x00,0x00,0xe0,0x10,0x10,0x10,0xe0,0x00,0x00,0xc0,0x80,0x40,0x80,0x00,0x00,0x80,0x40,0x40,0x80,0x00,0x00,0xcc,0x8e,0x4f,0x8f,0x0f,0x07,0x03,0x01,0x00,0x00,0x00,0x60,0x90,0x90,0x10,0x00,0x00,0x00,0x80,0x41,0x43,0x87,0x0f,0x0f,0xcf,0x0e,0x0c,0xc0,0x00,0x00,0xc0,0x80,0x40,0xc0,0x00,0x00,0x80,0x40,0x40,0x40,0x00,0x00,0x80,0x40,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x07,0x08,0x08,0x08,0x07,0x00,0x00,0x3f,0x04,0x08,0x07,0x00,0x00,0x07,0x09,0xc9,0x49,0x40,0x40,0x8f,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x08,0x09,0x06,0x00,0x00,0x07,0x08,0x08,0x07,0x00,0x00,0x07,0x08,0x04,0x0f,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x40,0x07,0x08,0x08,0x08,0x00,0x00,0x07,0x09,0x09,0x09,0x00,0x00,0x00,0xc8,0x00,0x00,0x08,0xc0,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x3f,0x04,0x04,0x04,0x03,0x00,0x00,0x1e,0x21,0x21,0x1e,0x00,0x07,0x38,0x0e,0x0e,0x38,0x07,0x00,0x1e,0x25,0x25,0x26,0x00,0x00,0x3f,0x02,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x3f,0x00,0x00,0x3f,0x02,0x01,0x3e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x3c,0x03,0x00,0x00,0x00,0x1e,0x21,0x21,0x1e,0x00,0x00,0x1f,0x20,0x10,0x3f,0x00,0x3f,0x02,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x02,0x02,0x02,0x3f,0x00,0x00,0x19,0x25,0x15,0x1e,0x20,0x00,0x3f,0x02,0x01,0x3e,0x00,0x1e,0x21,0x12,0x3f,0x00,0x00,0x22,0x25,0x29,0x11,0x00, -}; - -const unsigned char opentxtaranis_splash[] = { - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0xff,0xff,0x7f,0x3f,0x3f,0x7f,0x1f,0x3f,0x3f,0x1f,0x1f,0x0f,0x0f,0x1f,0x1f,0x07,0x0f,0x0f,0x07,0x0f,0x0f,0x07,0x07,0x03,0x07,0x07,0x03,0x03,0x03,0x07,0x07,0x03,0x07,0x03,0x83,0x07,0x83,0x03,0x03,0x03,0x03,0x83,0x03,0x03,0x03,0x83,0x83,0x07,0x83,0x03,0x07,0x03,0x07,0x03,0x03,0x03,0x03,0x07,0x07,0x03,0x07,0x07,0x07,0x0f,0x0f,0x07,0x1f,0x07,0x0f,0x0f,0x1f,0x0f,0x0f,0x1f,0x1f,0x3f,0x3f,0x1f,0x7f,0x3f,0x3f,0x7f,0xff,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x83,0x83,0x83,0xc3,0x83,0x83,0x83,0xc3,0xc3,0xc3,0xc3,0x83,0x83,0xc3,0x83,0x83,0x03,0x83,0x87,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x03,0x07,0x07,0x0f,0x07,0x07,0x1f,0x0f,0x07,0x0f,0x0f,0x2f,0x1f,0x1f,0x3f,0x7f,0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x43,0x03,0x83,0x83,0x83,0xc3,0x83,0x83,0x83,0xc3,0x83,0x83,0x83,0xc3,0x83,0x83,0x83,0xc3,0x83,0x83,0x83,0xc3,0x83,0x83,0x83,0xc3,0x83,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x0b,0x07,0x03,0x07,0x0f,0x07,0x1f,0x0f,0x1f,0x1f,0x1f,0x3f,0xbf,0xbf,0x7f,0x7f,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfd,0x03,0xff,0xff,0xff,0xff,0xfb,0x07,0xf7,0xf7,0xff,0x0f,0xcf,0xdf,0xdf,0x3f,0xdf,0xbf,0xbf,0x7f,0xff,0xff,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xff,0xff,0xff,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfb,0xfb,0xff,0xff,0xf3,0xf3,0xf3,0xfb,0xf3,0xd3,0xe3,0xe3,0xc3,0x83,0x83,0xc3,0x83,0x03,0x83,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x07,0x0b,0x03,0x07,0x3f,0x0f,0x0f,0x1f,0xff,0x3f,0x3f,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0x7f,0x7f,0xff,0x5f,0x1f,0x1f,0x3f,0x17,0x07,0x07,0x0f,0x07,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x83,0x43,0x83,0x83,0xe3,0xe3,0xe3,0xc3,0xe3,0xeb,0xf3,0xf3,0xfb,0xff,0xff,0xfb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x4f,0x09,0x7b,0x87,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x38,0x74,0xf8,0xfc,0xfc,0xfc,0xfe,0xff,0xfe,0xff,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xfe,0xff,0xfe,0xfc,0xfc,0xfc,0xfe,0xf0,0x38,0x74,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x4f,0x09,0x7b,0x87,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x0f,0x1f,0x0f,0x1f,0x17,0x0e,0x07,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x00,0xc0,0xc0,0x80,0xe0,0xc8,0xef,0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x07,0x07,0x07,0x0f,0x07,0x07,0x07,0x0f,0x07,0x07,0x07,0x0f,0x07,0x07,0x07,0x0f,0x07,0x07,0x07,0x0f,0x07,0x0f,0x0f,0xff,0x07,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0xf8,0xf8,0xf0,0xd0,0xd0,0xe0,0xe0,0xa0,0x80,0x80,0xc0,0x80,0x00,0x80,0x00,0x01,0x01,0x01,0x00,0x05,0x03,0x01,0x03,0x03,0x07,0x0f,0x07,0x0f,0x0f,0x0f,0x1f,0x5f,0x5f,0x3f,0x3f,0xff,0xff,0xff,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xfe,0xfe,0xff,0x01,0xfb,0xf9,0xfb,0x07,0xfb,0xf7,0xf7,0x0f,0xef,0xff,0xef,0x1f,0x5f,0xdf,0x7f,0xbf,0xbf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xfe,0xfc,0xfe,0xf8,0xfc,0xfc,0xf8,0xe0,0xe8,0xf0,0xf0,0xc0,0xc0,0xc0,0xe0,0xc0,0x40,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x01,0x07,0x03,0x03,0x07,0x02,0x03,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x80,0x00,0xa0,0xc0,0x80,0xc0,0xc0,0xe0,0xf0,0xe0,0xf8,0xf0,0xf0,0xf8,0xfe,0xfa,0xfc,0xfc,0xfe,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xfe,0xff,0xfa,0xf4,0xfc,0xf8,0xf8,0xe0,0xe0,0xf0,0xc0,0xe0,0xe0,0xc0,0x80,0x80,0x80,0xc0,0x00,0x00,0x80,0x80,0x00,0x01,0x80,0x00,0x01,0x03,0x03,0x01,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x07,0x0f,0x07,0x0f,0x07,0x07,0x07,0x07,0x0f,0x07,0x07,0x07,0x0f,0x0f,0x07,0x0f,0x07,0x07,0x03,0x07,0x07,0x07,0x03,0x07,0x03,0x01,0x03,0x03,0x01,0x00,0x01,0x80,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0xc0,0xc0,0xe0,0xe0,0xc0,0xf8,0xe0,0xe0,0xf0,0xfa,0xf4,0xfc,0xf8,0xfe,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0xfc,0xfc,0xfe,0xfc,0xfc,0xfc,0xfe,0xfc,0xfc,0xfc,0xfe,0xfc,0xfc,0xfc,0xfe,0xfc,0xfc,0xfc,0xfe,0xfe,0xfe,0xfc,0xfe,0xff,0xfe,0xfe,0xfe,0xff,0xff,0xff,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x0f,0x0f,0x0f,0x1f,0x0f,0x0f,0x0f,0x1f,0x0f,0x0f,0x0f,0x1f,0x0f,0x0f,0x0f,0x1f,0x0f,0x0f,0x0f,0x1f,0x0f,0x0f,0x0f,0x1f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfd,0xfe,0xfc,0xfe,0xf4,0xfc,0xfc,0xf8,0xe8,0xe8,0xf0,0xf0,0xc0,0xc0,0xc0,0xe0,0x00,0x80,0xc0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x7f,0x7f,0x7f,0x80,0x3f,0xff,0xbf,0xc0,0xcf,0xcf,0xef,0xf0,0xef,0xf7,0xf7,0xf8,0xff,0xff,0xfb,0xfc,0xfe,0xfc,0xfe,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xbf,0x7f,0x7f,0x1f,0x3f,0x1f,0x3f,0x1f,0x0f,0x1f,0x0f,0x03,0x0b,0x07,0x07,0x01,0x01,0x01,0x03,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xc0,0xc0,0xe0,0x00,0xc0,0xc0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x02,0x01,0x01,0x03,0x03,0x03,0x07,0x07,0x17,0x0f,0x0f,0x1f,0x3f,0x3f,0x1f,0xff,0x7f,0x3f,0x7f,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0x0f,0x1f,0xff,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xee,0xff,0x1f,0xee,0xee,0xfe,0x1f,0xee,0xee,0xff,0x1e,0xee,0xef,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xef,0xfe,0x1e,0xee,0xee,0xff,0x1e,0xee,0xee,0xfe,0x1f,0xef,0xee,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xee,0xee,0xfe,0x1f,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xee,0xee,0xfe,0x1f,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xef,0xef,0xff,0x1f,0xee,0xef,0xfe,0x1f,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xee,0xee,0xfe,0x1e,0xfe,0x0e,0x1e,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0xe0,0xff,0xff,0xe7,0xe7,0xf7,0xf8,0xf7,0xfb,0xfb,0xfc,0xff,0xff,0xfd,0xfe,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0xff,0x7f,0x1f,0x5f,0x3f,0x3f,0x0f,0x0f,0x0f,0x1f,0x03,0x07,0x0f,0x07,0x07,0x03,0x01,0x03,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x80,0x80,0xc0,0xc0,0xf0,0xf0,0xe0,0xf4,0xf4,0xf8,0xf8,0xfc,0xff,0xfc,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfe,0xff,0xf8,0xfe,0xf8,0xfc,0xe8,0xe8,0xf0,0xf0,0xa0,0xc0,0xe0,0xc0,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x03,0x01,0x01,0x03,0x0f,0x0b,0x07,0x07,0x1f,0x1f,0x1f,0x0f,0x5f,0x1f,0x3f,0x3f,0xff,0x7f,0xff,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, -}; - -const unsigned char ersky9x_splash[] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xE0,0xE0,0xF0,0xF8,0xF8,0xFC,0xFC,0xFC,0xF8,0xF8,0xF8,0xF0,0x40,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xC0,0xE0,0xE0,0xE0,0xF0,0xF0,0xF0,0xE0,0xC0,0x00, - 0x00,0x00,0x00,0x00,0xC0,0xF8,0xFF,0xFF,0x9F,0x87,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x03,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0xC3,0xE3,0x73,0x3F,0x1F,0x00,0x00,0x00,0x00,0x80,0xE0,0xF0,0xF8,0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0x7F,0x3F,0x1F,0x1F,0x0F,0x07,0x83,0xC3,0xE1,0xE1,0xE0,0xE0,0xC0,0x00,0x0E,0x1F,0x3F,0x7F,0x7F,0xFF,0xF3,0xE0,0xE0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xF0,0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0x7F,0x3F,0x0F,0x03,0x01,0x01,0x01,0x00,0x00, - 0x00,0x80,0xF0,0xFE,0xFF,0xFF,0xC7,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0xC1,0x01,0xC1,0xF0,0xFE,0xFF,0x3F,0x0F,0x07,0x0F,0x0D,0x0D,0x0D,0x1D,0x19,0x19,0x19,0x19,0x31,0x31,0x31,0x31,0x71,0x61,0x61,0x61,0xE1,0xC1,0xC1,0xC1,0x81,0x00,0x00,0x00,0x80,0xE0,0xF8,0xFC,0xFF,0xFF,0xFF,0xFF,0x2F,0x17,0x0F,0x03,0x81,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x03,0x07,0x07,0x0E,0x1E,0x1C,0x38,0x70,0xE0,0xF0,0xF8,0xFE,0xFF,0xFF,0x7F,0x1F,0x0F,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x7E,0xE7,0xC3,0xC3,0xC3,0xC3,0xC7,0x8E,0x0C,0x00,0x00,0xFF,0xFF,0xC0,0xE0,0xF0,0x38,0x1C,0x0E,0x07,0x03,0x00,0x00,0x3F,0x7F,0xE0,0xC0,0x80,0x80,0xC0,0xE0,0x7F,0x3F,0x00,0x00,0x00,0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xF8,0xFC,0xFC,0xFE,0xFF,0x7F,0x3F,0x1F,0x0F,0x07,0xC3,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xE0,0xF8,0xFE,0xFF,0xFF,0x7F,0x1F,0x0F,0x1F,0x3D,0x7C,0xF8,0xF8,0xF0,0xE0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x1C,0x38,0x30,0x30,0x30,0x30,0x39,0x1F,0x0F,0x00,0x00,0x3F,0x3F,0x00,0x01,0x03,0x07,0x0E,0x1C,0x38,0x30,0x00,0x00,0x00,0x00,0x00,0x01,0x3F,0x3F,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x03,0x07,0x07,0x07,0x03,0x01,0x00,0x00,0x00,0xE0,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xF0,0x78,0xDE,0x27,0x13,0x09,0x0E,0x07,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x0F,0x7F,0xFF,0xFC,0x78,0x78,0xF8,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xF0,0xFE,0xFF,0xFF,0xFF,0xFF,0x3F,0x0F,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xE0,0x38,0x1E,0x0F,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x0E,0x02,0x02,0x02,0x02,0x02,0x03,0x03,0x00,0x0F,0x0F,0x00,0x0F,0x0F,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x0F,0x0F,0x08,0x08,0x08,0x08,0x08,0x0F,0x0F,0x00,0x00,0x00,0x00,0x0F,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xF8,0xBF,0xFF,0xFF,0xFF,0x1F,0x7F,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFC,0xFF,0x7F,0x78,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x1C,0x07,0x0E,0x0F,0x07,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; diff --git a/companion/src/storage/appdata.cpp b/companion/src/storage/appdata.cpp index 931381b5b..0f4b0a5e3 100644 --- a/companion/src/storage/appdata.cpp +++ b/companion/src/storage/appdata.cpp @@ -335,7 +335,6 @@ void Profile::resetFwVariables() CompStoreObj::resetProperty(this, name); } - // ** ComponentAssetData class******************** ComponentAssetData::ComponentAssetData() : CompStoreObj(), index(-1) @@ -727,6 +726,14 @@ void AppData::convertSettings(QSettings & settings) } } + if (savedMajMin < 0x300) { + // 3.0 CloudBuild copy filter changed to cater for uf2 + qInfo().noquote() << "Deleting CloudBuild settings to force refresh"; + static const QString path = QStringLiteral("Components/component6"); + if (settings.contains(path)) + settings.remove(path); + } + if (removeUnused) clearUnusedSettings(settings); diff --git a/companion/src/storage/appdata.h b/companion/src/storage/appdata.h index 119dd8169..1ca124a17 100644 --- a/companion/src/storage/appdata.h +++ b/companion/src/storage/appdata.h @@ -53,7 +53,7 @@ #define APP_COMPANION QStringLiteral("EdgeTX Companion") #define APP_SIMULATOR QStringLiteral("EdgeTX Simulator") -//! Default location for EdgeTX-related user documents (settigns, logs, etc) +//! Default location for EdgeTX-related user documents (settings, logs, etc) #define CPN_DOCUMENTS_LOCATION QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) % "/" % COMPANY //! Location for settings backup files. TODO: make option or remember last location. #define CPN_SETTINGS_BACKUP_DIR CPN_DOCUMENTS_LOCATION % "/backup" @@ -97,10 +97,10 @@ T m_name; // Member variable. \endcode - If you only use this macro (w/out a \p PROPERTY*() macro) then you need to provide the getter, setter, and reset functions yourself. The initializer functoin is optional. + If you only use this macro (w/out a \p PROPERTY*() macro) then you need to provide the getter, setter, and reset functions yourself. The initializer function is optional. Those function names must match the expected signatures as defined in the \e Q_PROPERTY() macro. - \param TYPE The data type (T). + \param TYPE The data type (T). \param NAME Name of the property. \param KEY Settings key name, a const string. Can be the same as \a NAME. \param DFLT Default value, must be compatible with \a TYPE. @@ -185,7 +185,7 @@ /*! - \brief CompStoreObj manages staic meta data on member properties (extending Qt's own property system) and provides utility functions for subclasses. + \brief CompStoreObj manages static meta data on member properties (extending Qt's own property system) and provides utility functions for subclasses. The extension is essentially used to provide some "integration" with \e QSettings by providing storage path/key mappings to properties. It also adds ability to access property default values, auto-initialization from saved settings (with fallback to default), and provides change notification signals. @@ -467,6 +467,7 @@ class Profile: public CompStoreObj Q_OBJECT public: Profile & operator=(const Profile & rhs); + QString getVariantFromType() const { return fwType().section("-", 1, 1); } public slots: bool existsOnDisk(); @@ -756,18 +757,7 @@ class AppData: public CompStoreObj PROPERTYQBA (mdiWinGeo) PROPERTYQBA (mdiWinState) PROPERTYQBA (compareWinGeo) - - PROPERTYSTR3(armMcu, "arm_mcu", QStringLiteral("at91sam3s4-9x")) - PROPERTYSTR2(avrArguments, "avr_arguments") - PROPERTYSTR2(avrPort, "avr_port") - PROPERTYSTR2(avrdudeLocation, "avrdudeLocation") - PROPERTYSTR3(dfuArguments, "dfu_arguments", QStringLiteral("-a 0")) - PROPERTYSTR2(dfuLocation, "dfu_location") - PROPERTYSTR2(sambaLocation, "samba_location") - PROPERTYSTR3(sambaPort, "samba_port", QStringLiteral("\\USBserial\\COM23")) - PROPERTYSTR2(libDir, "libraryPath") - PROPERTYSTR3(backupDir, "backupPath", CPN_DOCUMENTS_LOCATION % "/backups") PROPERTYSTR3(eepromDir, "lastDir", CPN_DOCUMENTS_LOCATION % "/eeproms") PROPERTYSTR3(flashDir, "lastFlashDir", CPN_DOCUMENTS_LOCATION % "/flash") @@ -789,13 +779,10 @@ class AppData: public CompStoreObj PROPERTYSTR (locale) PROPERTYSTR (gePath) - PROPERTYSTRD(mcu, QStringLiteral("m64")) - PROPERTYSTRD(programmer, QStringLiteral("usbasp")) PROPERTY(NewModelAction, newModelAction, MODEL_ACT_WIZARD) PROPERTY4(int, embedSplashes, "embedded_splashes", 0) - PROPERTY4(int, fwServerFails, "fwserver", 0) PROPERTY4(int, iconSize, "icon_size", 2) PROPERTY4(int, historySize, "history_size", 10) PROPERTY (int, generalEditTab, 0) diff --git a/companion/src/storage/firmwareinterface.cpp b/companion/src/storage/firmwareinterface.cpp index c096fb6e2..b3974fa3b 100644 --- a/companion/src/storage/firmwareinterface.cpp +++ b/companion/src/storage/firmwareinterface.cpp @@ -20,7 +20,6 @@ */ #include "hexinterface.h" -#include "splash.h" #include "firmwareinterface.h" #include "helpers.h" #include "storage.h" @@ -85,14 +84,18 @@ public: { while(pos 0) { - flavour = seekLabel(FW_MARK); + type = (flash.startsWith("UF2\n") ? FIRMWARE_TYPE_UF2 : FIRMWARE_TYPE_BIN); + flavour = seekLabel(FW_MARK).remove("edgetx-"); version = seekLabel(VERS_MARK); + if (version.startsWith("opentx-")) { // old version format int index = version.lastIndexOf('-'); flavour = version.mid(0, index); version = version.mid(index + 1); } + date = seekLabel(DATE_MARK); time = seekLabel(TIME_MARK); eepromId = seekLabel(EEPR_MARK); @@ -200,8 +203,7 @@ FirmwareInterface::FirmwareInterface(const QString & filename, QDialog* parentDi QStringList list = eepromId.split('-'); eepromVersion = list[0].toInt(); eepromVariant = list[1].toInt(); - } - else { + } else { eepromVersion = eepromId.toInt(); } @@ -215,16 +217,20 @@ QString FirmwareInterface::seekString(const QString & string) QString result = ""; int start = flash.indexOf(string.toUtf8()); + if (start > 0) { start += string.length(); int end = -1; - for (int i=start; i 0) { result = flash.mid(start, (end - start)).trimmed(); } @@ -236,51 +242,44 @@ QString FirmwareInterface::seekString(const QString & string) QString FirmwareInterface::seekLabel(const QString & label) { QString result = seekString(label + "\037\033:"); + if (result.isEmpty()) { result = seekString(label + "\037\075:"); // This is for Horus } + if (!result.isEmpty()) { return result; } - return seekString(label + ":"); -} -QString FirmwareInterface::getFlavour() const -{ - if (flavour == "opentx-taranis-x9e") - return "opentx-x9e"; - else if (flavour == "opentx-x9dp" || flavour == "opentx-taranis-plus") - return "opentx-x9d+"; - else if (flavour == "opentx-taranis") - return "opentx-x9d"; - else if (flavour == "opentx-horus") - return "opentx-x12s"; - else - return flavour; + return seekString(label + ":"); } bool FirmwareInterface::isHardwareCompatible(const FirmwareInterface &previousFirmware) const { QString newFlavour = getFlavour(); + if (newFlavour.isEmpty()) { return true; } + QString previousFlavour = previousFirmware.getFlavour(); + if (previousFlavour.isEmpty()) { return true; } + return (newFlavour == previousFlavour); } bool FirmwareInterface::seekSplash(QByteArray splash) { int start = flash.indexOf(splash); - if (start>0) { + + if (start > 0) { splashOffset = start; splashSize = splash.size(); return true; - } - else { + } else { return false; } } @@ -288,20 +287,23 @@ bool FirmwareInterface::seekSplash(QByteArray splash) bool FirmwareInterface::seekSplash(QByteArray sps, QByteArray spe, int size) { int start = 0; - while (start>=0) { + + while (start >= 0) { start = flash.indexOf(sps, start+1); + if (start>0) { int end = start + sps.size() + size; + if (end == flash.indexOf(spe, end)) { splashOffset = start + sps.size(); splashSize = end - start - sps.size(); return true; - } - else { + } else { qDebug() << flash.indexOf(spe, start) << end << sps.size() << spe; } } } + return false; } @@ -317,30 +319,7 @@ void FirmwareInterface::seekSplash() splashOffset = 0; splashWidth = SPLASH_WIDTH; splashHeight = SPLASH_HEIGHT; - splash_format = QImage::Format_Mono; - - if (seekSplash(QByteArray((const char *)gr9x_splash, sizeof(gr9x_splash))) || seekSplash(QByteArray((const char *)gr9xv4_splash, sizeof(gr9xv4_splash)))) { - return; - } - - if (seekSplash(QByteArray((const char *)er9x_splash, sizeof(er9x_splash)))) { - return; - } - - if (seekSplash(QByteArray((const char *)opentx_splash, sizeof(opentx_splash)))) { - return; - } - - if (seekSplash(QByteArray((const char *)opentxtaranis_splash, sizeof(opentxtaranis_splash)))) { - splashWidth = SPLASHX9D_WIDTH; - splashHeight = SPLASHX9D_HEIGHT; - splash_format = QImage::Format_Indexed8; - return; - } - - if (seekSplash(QByteArray((const char *)ersky9x_splash, sizeof(ersky9x_splash)))) { - return; - } + splashFormat = QImage::Format_Mono; if (seekSplash(QByteArray(ETX_SPS_9X, ETX_SPS_SIZE), QByteArray(ETX_SPE, ETX_SPE_SIZE), 1024)) { return; @@ -349,18 +328,9 @@ void FirmwareInterface::seekSplash() if (seekSplash(QByteArray(ETX_SPS_TARANIS, ETX_SPS_SIZE), QByteArray(ETX_SPE, ETX_SPE_SIZE), RLE_SPLASH_MAX_SIZE)) { splashWidth = SPLASHX9D_WIDTH; splashHeight = SPLASHX9D_HEIGHT; - splash_format = QImage::Format_Indexed8; + splashFormat = QImage::Format_Indexed8; return; } - - if (seekSplash(QByteArray(ERSKY9X_SPS, sizeof(ERSKY9X_SPS)), QByteArray(ERSKY9X_SPE, sizeof(ERSKY9X_SPE)), 1030)) { - return; - } - - if (seekSplash(QByteArray(ERSPLASH_MARKER, sizeof(ERSPLASH_MARKER)))) { - splashOffset += sizeof(ERSPLASH_MARKER); - splashSize = sizeof(er9x_splash); - } } bool FirmwareInterface::setSplash(const QImage & newsplash) @@ -372,36 +342,38 @@ bool FirmwareInterface::setSplash(const QImage & newsplash) uint8_t b[SPLASH_SIZE_MAX] = {0}; QColor color; QByteArray splash; - if (splash_format == QImage::Format_Indexed8) { - for (unsigned int y=0; y RLE_SPLASH_MAX_SIZE){ - if(parentDialog) + if (splashSize > RLE_SPLASH_MAX_SIZE){ + if (parentDialog) QMessageBox::critical(parentDialog, CPN_STR_TTL_ERROR, QObject::tr("Compressed image size exceeds reserved space.")); return false; } } - } - else { + } else { QColor black = QColor(0,0,0); QImage blackNwhite = newsplash.convertToFormat(QImage::Format_MonoLSB); - for (uint y=0; y data; data.resize(splashWidth * splashHeight); RleBitmap img((uint8_t*)flash.data() + splashOffset - 2, 0); img.to(&data[0], data.size()); + if (splashOffset > 0) { for (unsigned int y=0; y> 4) : (byte & 0x0F); @@ -454,6 +428,7 @@ QImage FirmwareInterface::getSplash() if (splashOffset > 0) { for (unsigned int y=0; y> 4) : (byte & 0x0F); @@ -498,24 +473,12 @@ unsigned int FirmwareInterface::save(const QString & filename) memcpy(binflash, flash.constData(), flashSize); QFile file(filename); - int fileType = getStorageType(filename); + if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { + free(binflash); + return -1; + } - if (fileType == STORAGE_TYPE_HEX) { - if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { //reading HEX TEXT file - free(binflash); - return -1; - } - QTextStream outputStream(&file); - HexInterface hex=HexInterface(outputStream); - hex.save(binflash, flashSize); - } - else { - if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { //reading HEX TEXT file - free(binflash); - return -1; - } - file.write((char*)binflash, flashSize); - } + file.write((char*)binflash, flashSize); file.close(); diff --git a/companion/src/storage/firmwareinterface.h b/companion/src/storage/firmwareinterface.h index a6380e586..1b72446b6 100644 --- a/companion/src/storage/firmwareinterface.h +++ b/companion/src/storage/firmwareinterface.h @@ -19,8 +19,7 @@ * GNU General Public License for more details. */ -#ifndef _FIRMWAREINTERFACE_H_ -#define _FIRMWAREINTERFACE_H_ +#pragma once #include #include @@ -46,11 +45,19 @@ class FirmwareInterface Q_DECLARE_TR_FUNCTIONS("FirmwareInterface") public: - FirmwareInterface(const QString & filename, QDialog* parentDialog = nullptr); + enum FirmwareType{ + FIRMWARE_TYPE_BIN, + FIRMWARE_TYPE_UF2 + }; + + explicit FirmwareInterface(const QString &filename, QDialog* parent = nullptr); + explicit FirmwareInterface(const QByteArray &flashData, QDialog* parent = nullptr); + inline QString getDate() { return date; } inline QString getTime() { return time; } - int getSize() { return flashSize; } - QString getFlavour() const; + inline const QByteArray& getFlash() { return flash; } + inline int getSize() { return flashSize; } + inline QString getFlavour() const { return flavour; } bool isHardwareCompatible(const FirmwareInterface &previousFirmware) const; inline QString getVersion() { return version; } inline int getEEpromVersion() { return eepromVersion; } @@ -64,31 +71,34 @@ class FirmwareInterface QImage::Format getSplashFormat(); unsigned int save(const QString & fileName); bool isValid(); + FirmwareType getType() { return type; } + const QString typeFileExtn() const { return (type == FIRMWARE_TYPE_UF2 ? "uf2" : "bin"); } private: QDialog* parentDialog; QByteArray flash; - uint flashSize; - QString seekString(const QString & string); - QString seekLabel(const QString & label); - void seekSplash(); - bool seekSplash(QByteArray sps, QByteArray spe, int size); - bool seekSplash(QByteArray splash); + uint flashSize = 0; QString filename; QString date; QString time; QString flavour; QString version; QString eepromId; - int eepromVersion; - int eepromVariant; + int eepromVersion = 0; + int eepromVariant = 0; QByteArray splash; - uint splashOffset; - uint splashSize; - uint splashWidth; - uint splashHeight; - QImage::Format splash_format; - bool isValidFlag; -}; + uint splashOffset = 0; + uint splashSize = 0; + uint splashWidth = 0; + uint splashHeight = 0; + QImage::Format splashFormat; + bool isValidFlag = false; + FirmwareType type; -#endif // _FIRMWAREINTERFACE_H_ + void initFlash(const QByteArray& flashData); + QString seekString(const QString & string); + QString seekLabel(const QString & label); + void seekSplash(); + bool seekSplash(QByteArray sps, QByteArray spe, int size); + bool seekSplash(QByteArray splash); +}; diff --git a/companion/src/thirdparty/qcustomplot/qcustomplot.cpp b/companion/src/thirdparty/qcustomplot/qcustomplot.cpp index f3f1c47af..3ad01bae9 100644 --- a/companion/src/thirdparty/qcustomplot/qcustomplot.cpp +++ b/companion/src/thirdparty/qcustomplot/qcustomplot.cpp @@ -32173,9 +32173,8 @@ QCPPolarAxisRadial::SelectablePart QCPPolarAxisRadial::getPartAt(const QPointF & return spTickLabels; else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint())) return spAxisLabel; - else - return spNone; - */ + else */ + return spNone; } /* inherits documentation from base class */ @@ -32994,8 +32993,10 @@ QCPPolarAxisAngular::SelectablePart QCPPolarAxisAngular::getPartAt(const QPointF return spTickLabels; else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint())) return spAxisLabel; - else */ + else return spNone; + */ + return spNone; // until TODO fix } /* inherits documentation from base class */ diff --git a/companion/src/updates/CMakeLists.txt b/companion/src/updates/CMakeLists.txt index cce0e8258..d7c8362b3 100644 --- a/companion/src/updates/CMakeLists.txt +++ b/companion/src/updates/CMakeLists.txt @@ -50,6 +50,7 @@ add_library(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME} PRIVATE ${CPN_COMMON_LIB} + ${RS_DFU_LIBRARIES} ) target_include_directories(${PROJECT_NAME} @@ -58,4 +59,5 @@ target_include_directories(${PROJECT_NAME} "${CMAKE_CURRENT_BINARY_DIR}" PUBLIC "${CMAKE_CURRENT_LIST_DIR}" + "${RS_DFU_INCLUDE_DIRS}" ) diff --git a/companion/src/updates/updatecloudbuild.cpp b/companion/src/updates/updatecloudbuild.cpp index 6398b619c..c2cc18c7b 100644 --- a/companion/src/updates/updatecloudbuild.cpp +++ b/companion/src/updates/updatecloudbuild.cpp @@ -24,11 +24,14 @@ #include "appdata.h" #include "eeprominterface.h" #include "flashfirmwaredialog.h" +#include "firmwareinterface.h" +#include "radiointerface.h" // these must match text strings in server response constexpr char CLOUD_BUILD_WAITING[] {"WAITING_FOR_BUILD"}; constexpr char CLOUD_BUILD_IN_PROGRESS[] {"BUILD_IN_PROGRESS"}; constexpr char CLOUD_BUILD_SUCCESS[] {"BUILD_SUCCESS"}; +constexpr char CLOUD_BUILD_ERROR[] {"BUILD_ERROR"}; UpdateCloudBuild::UpdateCloudBuild(QWidget * parent) : UpdateInterface(parent, CID_CloudBuild, tr("CloudBuild"), Repo::REPO_TYPE_BUILD, @@ -75,6 +78,8 @@ int UpdateCloudBuild::cloudStatusToInt(const QString status) return STATUS_IN_PROGRESS; else if (status == QString(CLOUD_BUILD_SUCCESS)) return STATUS_SUCCESS; + else if (status == QString(CLOUD_BUILD_ERROR)) + return STATUS_ERROR; else return STATUS_UNKNOWN; } @@ -95,7 +100,7 @@ void UpdateCloudBuild::assetSettingsInit() cad.filter("%FWFLAVOUR%"); cad.destSubDir("FIRMWARE"); cad.copyFilterType(UpdateParameters::UFT_Pattern); - cad.copyFilter("^%FWFLAVOUR%-%LANGUAGE%.*-%RELEASE%\\.bin$"); + cad.copyFilter("^%FWFLAVOUR%-%LANGUAGE%.*-%RELEASE%\\.(bin|uf2)$"); cad.maxExpected(1); qDebug() << "Asset settings initialised"; @@ -210,6 +215,22 @@ int UpdateCloudBuild::asyncInstall() int ret = QMessageBox::question(status()->progress(), CPN_STR_APP_NAME, tr("Write the updated firmware to the radio now ?"), QMessageBox::Yes | QMessageBox::No); if (ret == QMessageBox::Yes) { + QString extn(QFileInfo(destPath).suffix().toLower()); + QString bootmodes; + if (dfuFileExtensions().contains(extn)) + bootmodes.append("DFU"); + + if (uf2FileExtensions().contains(extn)) { + if (!bootmodes.isEmpty()) { + bootmodes.append(tr(" or ")); + } + bootmodes.append("UF2"); + } + + QMessageBox::warning(status()->progress(), tr("Write Firmware to Radio"), + tr("Before continuing, ensure the radio is connected and booted in %1 mode(s)") + .arg(bootmodes)); + FlashFirmwareDialog *dlg = new FlashFirmwareDialog(this); dlg->exec(); dlg->deleteLater(); @@ -398,6 +419,44 @@ void UpdateCloudBuild::cleanup() m_eventLoop.quit(); } +bool UpdateCloudBuild::downloadFlaggedAsset(const int row) +{ + if (!UpdateInterface::downloadFlaggedAsset(row)) + return false; + + // rename downloaded firmware file based on the file contents + // flashing processes use the file extension to decide on + // the flashing mode i.e. DFU or UF2 + QString path = QString("%1/A%2/%3").arg(downloadDir()).arg(repo()->assets()->id()).arg(repo()->assets()->downloadName()); + status()->reportProgress(tr("Renaming: %1").arg(path), QtDebugMsg); + + FirmwareInterface firmware(path); + if (firmware.isValid()) { + QFileInfo fi(path); + QString newfname = QString("%1/%2.%3").arg(fi.absoluteDir().path()).arg(fi.completeBaseName()).arg(firmware.typeFileExtn()); + + if (QFileInfo::exists(newfname) && !QFile(newfname).remove()) { + status()->reportProgress(tr("Unable to delete: %1").arg(newfname), QtCriticalMsg); + return false; + } + + QFile fw(path); + if (!fw.rename(newfname)) { + status()->reportProgress(tr("Unable to rename %1 to %2").arg(fw.fileName()).arg(newfname), QtCriticalMsg); + return false; + } + + QFileInfo newfi(fw); + repo()->assets()->setDownloadName(newfi.fileName()); + status()->reportProgress(tr("Renamed: %1").arg(newfi.fileName()), QtInfoMsg); + } else { + status()->reportProgress(tr("%1 is not a valid firmware file").arg(path), QtCriticalMsg); + return false; + } + + return true; +} + bool UpdateCloudBuild::getStatus() { m_jobStatus = STATUS_UNKNOWN; @@ -438,8 +497,9 @@ bool UpdateCloudBuild::objectExists(const QJsonObject & parent, const QString ch bool UpdateCloudBuild::setAssetDownload() { - // this format MUST align with the asset copy filter - QString name = QString("%1-%2%3-%4.bin").arg(m_radio).arg(params()->language.toLower()).arg(m_buildFlags/* has a leading hyphen*/).arg(repo()->releases()->name()); + // Note: this format MUST align with the asset copy filter + // the file is renamed post download to amend the extension + QString name = QString("%1-%2%3-%4.tmp").arg(m_radio).arg(params()->language.toLower()).arg(m_buildFlags/* has a leading hyphen*/).arg(repo()->releases()->name()); repo()->assets()->setDownloadName(name.toLower()); @@ -454,6 +514,7 @@ bool UpdateCloudBuild::setAssetDownload() if (artifacts[i].isObject()) { const QJsonObject &artifact = artifacts[i].toObject(); if (stringExists(artifact, "slug") && artifact.value("slug").toString() == "firmware") { + //qDebug() << artifact; if (stringExists(artifact, "download_url")) { repo()->assets()->setDownloadUrl(artifact.value("download_url").toString()); } diff --git a/companion/src/updates/updatecloudbuild.h b/companion/src/updates/updatecloudbuild.h index ea783be5f..c772497ee 100644 --- a/companion/src/updates/updatecloudbuild.h +++ b/companion/src/updates/updatecloudbuild.h @@ -49,6 +49,7 @@ class UpdateCloudBuild: public UpdateInterface int asyncInstall() override; void assetSettingsInit() override; bool buildFlaggedAsset(const int row) override; + bool downloadFlaggedAsset(const int row) override; private slots: void cancel(); diff --git a/companion/src/updates/updatefirmware.cpp b/companion/src/updates/updatefirmware.cpp index 5215a1b0a..b7cd2c9b6 100644 --- a/companion/src/updates/updatefirmware.cpp +++ b/companion/src/updates/updatefirmware.cpp @@ -21,6 +21,7 @@ #include "updatefirmware.h" #include "flashfirmwaredialog.h" +#include "radiointerface.h" UpdateFirmware::UpdateFirmware(QWidget * parent) : UpdateInterface(parent, CID_Firmware, tr("Firmware"), Repo::REPO_TYPE_GITHUB, @@ -46,7 +47,7 @@ void UpdateFirmware::assetSettingsInit() cad.maxExpected(1); cad.destSubDir("FIRMWARE"); cad.copyFilterType(UpdateParameters::UFT_Pattern); - cad.copyFilter("^%FWFLAVOUR%-.*\\.bin$"); + cad.copyFilter("^%FWFLAVOUR%-.*\\.(bin|uf2)$"); qDebug() << "Asset settings initialised"; } @@ -106,6 +107,22 @@ int UpdateFirmware::asyncInstall() int ret = QMessageBox::question(status()->progress(), CPN_STR_APP_NAME, tr("Write the updated firmware to the radio now ?"), QMessageBox::Yes | QMessageBox::No); if (ret == QMessageBox::Yes) { + QString extn(QFileInfo(destPath).suffix().toLower()); + QString bootmodes; + if (dfuFileExtensions().contains(extn)) + bootmodes.append("DFU"); + + if (uf2FileExtensions().contains(extn)) { + if (!bootmodes.isEmpty()) { + bootmodes.append(tr(" or ")); + } + bootmodes.append("UF2"); + } + + QMessageBox::warning(status()->progress(), tr("Write Firmware to Radio"), + tr("Before continuing, ensure the radio is connected and booted in %1 mode(s)") + .arg(bootmodes)); + FlashFirmwareDialog *dlg = new FlashFirmwareDialog(this); dlg->exec(); dlg->deleteLater(); diff --git a/companion/src/updates/updatenetwork.cpp b/companion/src/updates/updatenetwork.cpp index cd7fb41c7..d024104f2 100644 --- a/companion/src/updates/updatenetwork.cpp +++ b/companion/src/updates/updatenetwork.cpp @@ -62,15 +62,20 @@ void UpdateNetwork::cleanup() void UpdateNetwork::connectReplyCommon() { - connect(m_reply, QOverload::of(&QNetworkReply::errorOccurred), [=] (QNetworkReply::NetworkError code) { + connect(m_reply, QOverload::of(&QNetworkReply::errorOccurred), [&](QNetworkReply::NetworkError code) { m_success = false; m_status->reportProgress(tr("Network error has occurred. Error code: %1").arg(code), QtCriticalMsg); }); - connect(m_reply, &QNetworkReply::sslErrors, [=]() { + connect(m_reply, &QNetworkReply::sslErrors, [&]() { m_success = false; m_status->reportProgress(tr("Ssl library version: %1").arg(QSslSocket::sslLibraryVersionString()), QtCriticalMsg); }); + + connect(m_reply, &QNetworkReply::redirected, [&](const QUrl &url) { + m_finalUrl = url; + //qDebug() << "final url:" << m_finalUrl; + }); } void UpdateNetwork::convertBufferToJson(QJsonDocument * json) @@ -299,8 +304,10 @@ void UpdateNetwork::onGetFinished(QNetworkReply * reply, DownloadDataType type) if (type == DDT_GitHub_SaveToFile || type == DDT_Build_SaveToFile) m_file->remove(); } - else + else { m_success = true; + m_status->reportProgress(tr("Download complete"), QtDebugMsg); + } if (type == DDT_GitHub_SaveToFile || type == DDT_Build_SaveToFile) { delete m_file; diff --git a/companion/src/updates/updatenetwork.h b/companion/src/updates/updatenetwork.h index 4a9183a16..e56ea3d47 100644 --- a/companion/src/updates/updatenetwork.h +++ b/companion/src/updates/updatenetwork.h @@ -71,6 +71,8 @@ class UpdateNetwork : public QObject static QString downloadDataTypeToString(const DownloadDataType val); + QUrl getFinalUrl() const { return m_finalUrl; } + private slots: void onGetFinished(QNetworkReply * reply, DownloadDataType ddt); void onPostFinished(); @@ -85,6 +87,7 @@ class UpdateNetwork : public QObject QByteArray *m_buffer; QFile *m_file; QUrl m_url; + QUrl m_finalUrl; bool m_success; QEventLoop m_eventLoop; QTimer m_timer; diff --git a/companion/targets/linux/45-companion-taranis.rules b/companion/targets/linux/45-companion-taranis.rules deleted file mode 100644 index c46bd1641..000000000 --- a/companion/targets/linux/45-companion-taranis.rules +++ /dev/null @@ -1 +0,0 @@ -SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE:="0666" diff --git a/companion/targets/linux/45-usbasp.rules b/companion/targets/linux/45-usbasp.rules deleted file mode 100644 index 05b8f09a3..000000000 --- a/companion/targets/linux/45-usbasp.rules +++ /dev/null @@ -1 +0,0 @@ -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="16c0", ATTR{idProduct}=="05dc", MODE="0666"