pax_global_header00006660000000000000000000000064146435616400014523gustar00rootroot0000000000000052 comment=30e7c38d37bd793295f6a003d2f33408094cc85b Heimdall-v2.1.0/000077500000000000000000000000001464356164000134305ustar00rootroot00000000000000Heimdall-v2.1.0/.builds/000077500000000000000000000000001464356164000147705ustar00rootroot00000000000000Heimdall-v2.1.0/.builds/alpine.yml000066400000000000000000000005651464356164000167710ustar00rootroot00000000000000image: alpine/edge sources: - https://git.sr.ht/~grimler/Heimdall artifacts: - Heimdall/build/bin/heimdall - Heimdall/build/bin/heimdall-frontend packages: - make - cmake - gcc - g++ - libc-dev - qt5-qtbase-dev - libusb-dev tasks: - build: | cd Heimdall mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. make -j8 Heimdall-v2.1.0/.builds/archlinux.yml000066400000000000000000000005031464356164000175060ustar00rootroot00000000000000image: archlinux sources: - https://git.sr.ht/~grimler/Heimdall artifacts: - Heimdall/build/bin/heimdall - Heimdall/build/bin/heimdall-frontend packages: - cmake - libusb - qt5-base tasks: - build: | cd Heimdall mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. make -j8 Heimdall-v2.1.0/.builds/ubuntu.yml000066400000000000000000000005671464356164000170450ustar00rootroot00000000000000image: ubuntu/lts sources: - https://git.sr.ht/~grimler/Heimdall artifacts: - Heimdall/build/bin/heimdall - Heimdall/build/bin/heimdall-frontend packages: - cmake - g++ - pkg-config - libusb-1.0-0-dev - qtbase5-dev - zlib1g-dev tasks: - build: | cd Heimdall mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. make -j8 Heimdall-v2.1.0/.gitignore000066400000000000000000000000401464356164000154120ustar00rootroot00000000000000.DS_Store ._* .idea/ /build/ *~ Heimdall-v2.1.0/CMakeLists.txt000066400000000000000000000006511464356164000161720ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) project(Heimdall) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) option(DISABLE_FRONTEND "Disable GUI frontend" OFF) add_subdirectory(libpit) add_subdirectory(heimdall) if(NOT DISABLE_FRONTEND) add_subdirectory(heimdall-frontend) add_dependencies(heimdall-frontend heimdall) endif() Heimdall-v2.1.0/LICENSE000066400000000000000000000021361464356164000144370ustar00rootroot00000000000000Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Copyright (c) 2021-2024 Henrik Grimler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Heimdall-v2.1.0/README.md000066400000000000000000000043741464356164000147170ustar00rootroot00000000000000# Heimdall [![builds.sr.ht status](https://builds.sr.ht/~grimler/Heimdall/commits/ubuntu.yml.svg)](https://builds.sr.ht/~grimler/Heimdall/commits/ubuntu.yml?) [![builds.sr.ht status](https://builds.sr.ht/~grimler/Heimdall/commits/archlinux.yml.svg)](https://builds.sr.ht/~grimler/Heimdall/commits/archlinux.yml?) [![builds.sr.ht status](https://builds.sr.ht/~grimler/Heimdall/commits/alpine.yml.svg)](https://builds.sr.ht/~grimler/Heimdall/commits/alpine.yml?) Heimdall is a cross-platform open-source tool suite used to flash firmware (aka ROMs) onto Samsung mobile devices. ## Supported Platforms Heimdall should work on most Linux systems, and perhaps even on OSX and Windows, though the latter two are not tested by the current maintainer. ## How does Heimdall work? Heimdall connects to a mobile device over USB and interacts with low-level software running on the device, known as Loke. Loke and Heimdall communicate via the custom Samsung-developed protocol typically referred to as the 'Odin 3 protocol'. USB communication in Heimdall is handled by the popular open-source USB library, [libusb](https://libusb.info). ## Free & Open Source Heimdall is both free and open source. It is licensed under the MIT license (see LICENSE). Heimdall has been developed through countless hours of reverse engineering work, predominantly by [Glass Echidna](https://glassechidna.com.au/), a _tiny_ independent software development company. ## Documentation To compile Heimdall and Heimdall-frontend (the gui application), run something like: ```sh mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release .. make ``` To only compile the CLI tool, add the option -DDISABLE_FRONTEND=true to the cmake command. The name of dependencies vary between distributions. On alpinelinux you need to install: `make cmake gcc g++ libc-dev qt5-qtbase-dev and libusb-dev`, on archlinux: `cmake libusb qt5-base` and on ubuntu: `cmake g++ pkg-config libusb-1.0.0-dev qtbase5-dev and zlib1g-dev`. Some more documentation, and instructions on how to use Heimdall and Heimdall-frontend, can be found in the doc/ folder. ### Odin protocol and PIT format For more details on the Odin protocol, and the PIT files, see the external project [samsung-loki/samsung-docs](https://samsung-loki.github.io/samsung-docs/). Heimdall-v2.1.0/cmake/000077500000000000000000000000001464356164000145105ustar00rootroot00000000000000Heimdall-v2.1.0/cmake/FindFseeko.cmake000066400000000000000000000066321464356164000175360ustar00rootroot00000000000000# CMake support for fseeko # # Based on FindLFS.cmake by # Copyright (C) 2016 Julian Andres Klode . # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # # This defines the following variables # # FSEEKO_DEFINITIONS - List of definitions to pass to add_definitions() # FSEEKO_COMPILE_OPTIONS - List of definitions to pass to add_compile_options() # FSEEKO_LIBRARIES - List of libraries and linker flags # FSEEKO_FOUND - If there is Large files support # include(CheckCSourceCompiles) include(FindPackageHandleStandardArgs) include(CMakePushCheckState) # Check for the availability of fseeko() # The cases handled are: # # * Native fseeko() # * Preprocessor flag -D_LARGEFILE_SOURCE # function(_fseeko_check) set(_fseeko_cppflags) cmake_push_check_state() set(CMAKE_REQUIRED_QUIET 1) set(CMAKE_REQUIRED_DEFINITIONS ${LFS_DEFINITIONS}) message(STATUS "Looking for native fseeko support") check_symbol_exists(fseeko stdio.h fseeko_native) cmake_pop_check_state() if (fseeko_native) message(STATUS "Looking for native fseeko support - found") set(FSEEKO_FOUND TRUE) else() message(STATUS "Looking for native fseeko support - not found") endif() if (NOT FSEEKO_FOUND) # See if it's available with _LARGEFILE_SOURCE. cmake_push_check_state() set(CMAKE_REQUIRED_QUIET 1) set(CMAKE_REQUIRED_DEFINITIONS ${LFS_DEFINITIONS} "-D_LARGEFILE_SOURCE") check_symbol_exists(fseeko stdio.h fseeko_need_largefile_source) cmake_pop_check_state() if (fseeko_need_largefile_source) message(STATUS "Looking for fseeko support with _LARGEFILE_SOURCE - found") set(FSEEKO_FOUND TRUE) set(_fseeko_cppflags "-D_LARGEFILE_SOURCE") else() message(STATUS "Looking for fseeko support with _LARGEFILE_SOURCE - not found") endif() endif() set(FSEEKO_DEFINITIONS ${_fseeko_cppflags} CACHE STRING "Extra definitions for fseeko support") set(FSEEKO_COMPILE_OPTIONS "" CACHE STRING "Extra compiler options for fseeko support") set(FSEEKO_LIBRARIES "" CACHE STRING "Extra definitions for fseeko support") set(FSEEKO_FOUND ${FSEEKO_FOUND} CACHE INTERNAL "Found fseeko") endfunction() if (NOT FSEEKO_FOUND) _fseeko_check() endif() find_package_handle_standard_args(FSEEKO "Could not find fseeko. Set FSEEKO_DEFINITIONS, FSEEKO_COMPILE_OPTIONS, FSEEKO_LIBRARIES." FSEEKO_FOUND) Heimdall-v2.1.0/cmake/FindLFS.cmake000066400000000000000000000140771464356164000167500ustar00rootroot00000000000000# CMake support for large files # # Copyright (C) 2016 Julian Andres Klode . # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # # This defines the following variables # # LFS_DEFINITIONS - List of definitions to pass to add_definitions() # LFS_COMPILE_OPTIONS - List of definitions to pass to add_compile_options() # LFS_LIBRARIES - List of libraries and linker flags # LFS_FOUND - If there is Large files support # include(CheckCSourceCompiles) include(FindPackageHandleStandardArgs) include(CMakePushCheckState) # Test program to check for LFS. Requires that off_t has at least 8 byte large set(_lfs_test_source " #include typedef char my_static_assert[sizeof(off_t) >= 8 ? 1 : -1]; int main(void) { return 0; } " ) # Check if the given options are needed # # This appends to the variables _lfs_cppflags, _lfs_cflags, and _lfs_ldflags, # it also sets LFS_FOUND to 1 if it works. function(_lfs_check_compiler_option var options definitions libraries) cmake_push_check_state() set(CMAKE_REQUIRED_QUIET 1) set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} ${options}) set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${definitions}) set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_DEFINITIONS} ${libraries}) message(STATUS "Looking for LFS support using ${options} ${definitions} ${libraries}") check_c_source_compiles("${_lfs_test_source}" ${var}) cmake_pop_check_state() if(${var}) message(STATUS "Looking for LFS support using ${options} ${definitions} ${libraries} - found") set(_lfs_cppflags ${_lfs_cppflags} ${definitions} PARENT_SCOPE) set(_lfs_cflags ${_lfs_cflags} ${options} PARENT_SCOPE) set(_lfs_ldflags ${_lfs_ldflags} ${libraries} PARENT_SCOPE) set(LFS_FOUND TRUE PARENT_SCOPE) else() message(STATUS "Looking for LFS support using ${options} ${definitions} ${libraries} - not found") endif() endfunction() # Check for the availability of LFS. # The cases handled are: # # * Native LFS # * Output of getconf LFS_CFLAGS; getconf LFS_LIBS; getconf LFS_LDFLAGS # * Preprocessor flag -D_FILE_OFFSET_BITS=64 # * Preprocessor flag -D_LARGE_FILES # function(_lfs_check) set(_lfs_cflags) set(_lfs_cppflags) set(_lfs_ldflags) set(_lfs_libs) cmake_push_check_state() set(CMAKE_REQUIRED_QUIET 1) message(STATUS "Looking for native LFS support") check_c_source_compiles("${_lfs_test_source}" lfs_native) cmake_pop_check_state() if (lfs_native) message(STATUS "Looking for native LFS support - found") set(LFS_FOUND TRUE) else() message(STATUS "Looking for native LFS support - not found") endif() if (NOT LFS_FOUND) # Check using getconf. If getconf fails, don't worry, the check in # _lfs_check_compiler_option will fail as well. execute_process(COMMAND getconf LFS_CFLAGS OUTPUT_VARIABLE _lfs_cflags_raw OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) execute_process(COMMAND getconf LFS_LIBS OUTPUT_VARIABLE _lfs_libs_tmp OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) execute_process(COMMAND getconf LFS_LDFLAGS OUTPUT_VARIABLE _lfs_ldflags_tmp OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) separate_arguments(_lfs_cflags_raw) separate_arguments(_lfs_ldflags_tmp) separate_arguments(_lfs_libs_tmp) # Move -D flags to the place they are supposed to be foreach(flag ${_lfs_cflags_raw}) if (flag MATCHES "-D.*") list(APPEND _lfs_cppflags_tmp ${flag}) else() list(APPEND _lfs_cflags_tmp ${flag}) endif() endforeach() # Check if the flags we received (if any) produce working LFS support _lfs_check_compiler_option(lfs_getconf_works "${_lfs_cflags_tmp}" "${_lfs_cppflags_tmp}" "${_lfs_libs_tmp};${_lfs_ldflags_tmp}") endif() if(NOT LFS_FOUND) # IRIX stuff _lfs_check_compiler_option(lfs_need_n32 "-n32" "" "") endif() if(NOT LFS_FOUND) # Linux and friends _lfs_check_compiler_option(lfs_need_file_offset_bits "" "-D_FILE_OFFSET_BITS=64" "") endif() if(NOT LFS_FOUND) # AIX _lfs_check_compiler_option(lfs_need_large_files "" "-D_LARGE_FILES=1" "") endif() set(LFS_DEFINITIONS ${_lfs_cppflags} CACHE STRING "Extra definitions for large file support") set(LFS_COMPILE_OPTIONS ${_lfs_cflags} CACHE STRING "Extra definitions for large file support") set(LFS_LIBRARIES ${_lfs_libs} ${_lfs_ldflags} CACHE STRING "Extra definitions for large file support") set(LFS_FOUND ${LFS_FOUND} CACHE INTERNAL "Found LFS") endfunction() if (NOT LFS_FOUND) _lfs_check() endif() find_package_handle_standard_args(LFS "Could not find LFS. Set LFS_DEFINITIONS, LFS_COMPILE_OPTIONS, LFS_LIBRARIES." LFS_FOUND) Heimdall-v2.1.0/cmake/Findlibusb.cmake000066400000000000000000000112201464356164000175670ustar00rootroot00000000000000# Based on module from bladeRF: https://github.com/Nuand/bladeRF/blob/e09200c38e1b273ebe43cd789839e66b0506aead/host/cmake/modules/FindLibUSB.cmake # This is a modified version of the file written by Hedrik Sattler, # from the OpenOBEX project (licensed GPLv2/LGPL). (If this is not correct, # please contact us so we can attribute the author appropriately.) # # https://github.com/zuckschwerdt/openobex/blob/master/CMakeModules/FindLibUSB.cmake # http://dev.zuckschwerdt.org/openobex/ # # Find libusb-1.0 # # It will use PkgConfig if present and supported, otherwise this # script searches for binary distribution in the path defined by # the LIBUSB_PATH variable. # # The following standard variables get defined: # LIBUSB_FOUND: true if LibUSB was found # LIBUSB_HEADER_FILE: the location of the C header file # LIBUSB_INCLUDE_DIRS: the directorys that contain headers # LIBUSB_LIBRARIES: the library files # LIBUSB_VERSION the detected libusb version # LIBUSB_HAVE_GET_VERSION True if libusb has libusb_get_version() include(CheckLibraryExists) include(CheckIncludeFile) # In Linux, folks should generally be able to simply fetch the libusb library and # development packages from their distros package repository. Windows users will # likely want to fetch a binary distribution, hence the Windows-oriented default. # # See https://github.com/libusb/libusb/releases/ if(WIN32) set(LIBUSB_PATH "C:/Program Files (x86)/libusb-1.0.20" CACHE PATH "Path to libusb files. (This is generally only needed for Windows users who downloaded binary distributions.)" ) endif() # FreeBSD has built-in libusb since 800069 if((${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") OR (${CMAKE_SYSTEM_NAME} MATCHES "kFreeBSD") ) exec_program(sysctl ARGS -n kern.osreldate OUTPUT_VARIABLE FREEBSD_VERSION) set(LIBUSB_FOUND TRUE) set(LIBUSB_SKIP_VERSION_CHECK TRUE) set(LIBUSB_INCLUDE_DIRS "/usr/include") set(LIBUSB_HEADER_FILE "${LIBUSB_INCLUDE_DIRS}/libusb.h") set(LIBUSB_LIBRARIES "usb") set(LIBUSB_LIBRARY_DIRS "/usr/lib/") endif((${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") OR (${CMAKE_SYSTEM_NAME} MATCHES "kFreeBSD") ) find_package(PkgConfig) if(PKG_CONFIG_FOUND) pkg_check_modules(PKGCONFIG_LIBUSB QUIET libusb-1.0 libusb) endif(PKG_CONFIG_FOUND) if(PKGCONFIG_LIBUSB_FOUND AND NOT LIBUSB_FOUND) set(LIBUSB_INCLUDE_DIRS ${PKGCONFIG_LIBUSB_INCLUDE_DIRS}) foreach(i ${PKGCONFIG_LIBUSB_LIBRARIES}) string(REGEX MATCH "[^-]*" ibase "${i}") find_library(${ibase}_LIBRARY NAMES ${i} PATHS ${PKGCONFIG_LIBUSB_LIBRARY_DIRS} ) if(${ibase}_LIBRARY) list(APPEND LIBUSB_LIBRARIES ${${ibase}_LIBRARY}) endif(${ibase}_LIBRARY) mark_as_advanced(${ibase}_LIBRARY) endforeach(i) else(PKGCONFIG_LIBUSB_FOUND) if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") # The libusbx binary distribution contains several libs. # Use the lib that got compiled with the same compiler. if(MSVC) if(CMAKE_CL_64) set(LIBUSB_LIBRARY_PATH_SUFFIX MS64/dll) else(CMAKE_CL_64) set(LIBUSB_LIBRARY_PATH_SUFFIX MS32/dll) endif(CMAKE_CL_64) elseif(CMAKE_COMPILER_IS_GNUCC) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(LIBUSB_LIBRARY_PATH_SUFFIX MinGW32/dll) else(CMAKE_SIZEOF_VOID_P EQUAL 8) set(LIBUSB_LIBRARY_PATH_SUFFIX MinGW64/dll) endif(CMAKE_SIZEOF_VOID_P EQUAL 8) endif(MSVC) else() set(LIBUSB_LIBRARY_PATH_SUFFIX lib) set(LIBUSB_EXTRA_PATHS /usr /usr/local /opt/local) endif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") find_file(LIBUSB_HEADER_FILE NAMES libusb.h PATHS ${LIBUSB_PATH} ${LIBUSB_EXTRA_PATHS} PATH_SUFFIXES include include/libusbx-1.0 include/libusb-1.0 ) mark_as_advanced(LIBUSB_HEADER_FILE) get_filename_component(LIBUSB_INCLUDE_DIRS "${LIBUSB_HEADER_FILE}" PATH) find_library(usb_LIBRARY NAMES libusb-1.0 usb-1.0 PATHS ${LIBUSB_PATH} ${LIBUSB_EXTRA_PATHS} PATH_SUFFIXES ${LIBUSB_LIBRARY_PATH_SUFFIX} ) mark_as_advanced(usb_LIBRARY) if(usb_LIBRARY) set(LIBUSB_LIBRARIES ${usb_LIBRARY}) endif(usb_LIBRARY) endif(PKGCONFIG_LIBUSB_FOUND AND NOT LIBUSB_FOUND) if(LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES) set(LIBUSB_FOUND TRUE) endif(LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES) if(LIBUSB_FOUND) set(CMAKE_REQUIRED_INCLUDES "${LIBUSB_INCLUDE_DIRS}") check_include_file("{LIBUSB_HEADER_FILE}" LIBUSB_FOUND) endif(LIBUSB_FOUND) Heimdall-v2.1.0/doc/000077500000000000000000000000001464356164000141755ustar00rootroot00000000000000Heimdall-v2.1.0/doc/Heimdall-Firmware-Packages.md000066400000000000000000000323161464356164000215310ustar00rootroot00000000000000Heimdall (c) 2010-2017 Benjamin Dobell, Glass Echidna https://www.glassechidna.com.au/products/heimdall/ DISCLAIMER: This software attempts to flash your Galaxy S device. The very nature of flashing is dangerous. As with all flashing software, Heimdall has the potential to damage (brick) your device if not used carefully. If you're concerned, don't use this software. Flashing ROMs onto your device may also void your warranty. Benjamin Dobell and Glass Echidna are not responsible for the result of your actions. # Flashing Heimdall Firmware Package with Heimdall Frontend As of Heimdall Frontend 1.3 there are now two main ways to flash a ROM from Heimdall Frontend. The simpler and preferred option is to download a Heimdall Firmware Package and follow the following steps. 1. Fully charge your device (use the wall charger as it's faster). 2. Open the Heimdall Frontend (heimdall-frontend) application. 3. From the "Load Package" tab, under the "Heimdall Firmware Package" section click the "Browse" button. 4. Use the dialogue that appears to navigate to, and select, the Heimdall firmware package that you wish to flash. 5. You will see progress bars appear as the package is decompressed and extracted. When the package has finished being decompressed you should see information about the particular firmware package that has been selected. 6. Verify that your device is listed under "Supported Devices". If it's not then STOP immediately! DO NOT flash this firmware to your device! Instead search for an appropriate firmware package for your device. If you believe there is a mistake and your device is actually supported please get in contact with the firmware developer (not Glass Echidna!) and ask them to rectify the issue. If the developer provided a URL you may be able to contact them by pressing the "Homepage" button. 7. If you've verified your device is supported you may continue to press the "Load / Customise" button. 8. You should now be looking at the "Flash" tab. If not verify that you did in fact push the "Load / Customise" button. Generally, you won't NEED or WANT to customise a firmware package! In which case you can safely move on to step 9. Nonetheless, the "Flash" tab provides you with a means to customise the firmware package before flashing it to your device. See "Performing a Custom Flash with Heimdall Frontend" for more details. 9. Put your Galaxy S device into download mode and plug it in to your PC. Download mode can be accessed several different ways depending on your particular device model. If you're unsure how to do this please search online for the appropriate method. 10. Press the "Start" button. 11. Heimdall Frontend will display the progress and inform you when the flash is complete. If something went wrong i.e. your device wasn't detected because it wasn't in download mode, then the status section will let you know the cause of the problem. # How to Create a Heimdall Firmware Package Firstly, Heimdall's firmware package format is just a regular TAR archive compressed with gzip. The only two real requirements are that a valid firmware.xml must be included (refer to Appendix A) and you can only include files (no directories, links etc.) As such if you'd like there is nothing preventing you from creating Heimdall packages manually. Of course Heimdall Frontend provides a simple user interface that takes care of all the hard work for you. There are two ways in which you can create a firmware package. You can create a package from scratch, or you can load an existing package, apply modifications and then save the package. Creating a package from scratch is the preferred approach, by taking this approach you're far less likely to run into file name length limitations. These are not Heimdall's own limitation but rather a limitation of the TAR archive format. Before you can access Heimdall Frontend's firmware creation functionality (available from the "Create Package" tab) you must first specify which files will be included in your package, as well as a few flashing options i.e. Whether or not users should repartition when flashing. This information must be filled out from the "Flash" tab in exactly the same fashion you would provide information to flash your device (see "Performing a Custom Flash with Heimdall Frontend"). As mentioned above, it's not the preferred means, but you're able to load an existing package as a starting point for this information. Once you've specified the files/partitions you wish to include in your firmware package the "Create Package" tab will become available. Clicking this tab will display additional information that you can include in your package. In order to continue you must fill out all sections except for the URLs section, which is optional. The following is a break-down of what all these options mean. - General Firmware Information: - Firmware Name - This is the name of your particular firmware. An example would be "Cyanogenmod". Firmware Version - This is the version identifier for your package. Any valid string will be accepted although a the inclusion of decimal point version number is preferred i.e. "7.1". If it makes sense then feel free to append a text string like "RC1" or "Beta 1" to the decimal point version. Platform Name - This is the name of the platform (or operating system) that your firmware is based on. In most cases this will simply be "Android". Platform Version - This is the operating system version that your firmware is based on. Again decimal point version numbers are preferred over text, i.e. "2.3.4" is preferred over "Gingerbread". - Developers - URLs (Optional): Homepage - Here you can enter your personal URL or a URL particularly pertaining to the firmware being packaged. The URL must be well formed for it to work. An example of a well formed URL is "https://www.glassechidna.com.au/products/heimdall/". It is important to include "https://" in order to specify the protocol as other protocols such as "ftp://" are equally valid although unlikely to be used. Donate - Here you can enter a URL that will link users to a page to make donations for the effort you've put into developing your firmware. Once again the URL must be well formed but there is no requirement on how your donation page should work. For instance both "https://www.glassechidna.com.au/donate/" and "https://forum.xda-developers.com/donatetome.php?u=2710388" are equally valid. Developer Info: Name - Here you can enter in the name of individual team members or a team name. Click "Add" and the developer will be added to the list on the right. If you make a mistake you can select a developer from the list and click "Remove". You can list as many developers as you like however visual constraints of the "Load Package" tab means only a few names will be visible. Where possible you may want to opt for team names over listing individual team members. - Supported Devices - This section allows you to create a list of devices that are supported by your particular firmware. Although Heimdall isn't capable of enforcing this we strongly recommend you take this section seriously. If filled out correctly you could help save a number of accidental bricks! Device Info: Manufacturer - This is where you can enter the name of the manufacturer for a particular device. For now this will most likely be "Samsung". Name - This is the human readable name for a particular device. "Galaxy S", "Galaxy S II", "Droid Charge", "Vibrant" and "Galaxy S (Telstra)" are all valid names. There are a lot of possible variations here so be as specific as you think is necessary. Product Code - This is by far the most important bit of device information. Device names tend to be region specific and further subject to the whims of telecommunication companies and resellers. Product Codes (or product IDs) are designated by manufacturers and are generally the definitive means of referring to a particular device. Examples are "GT-I9000", "GT-I9100" and "SCH-I897". If you're unsure of a particular product code then both Google and GSMArena are your friends! After filling out all the necessary information the "Build" button will be enabled. If it's still disabled then you know you're missing some required information. In particular you must specify at least one developer and at least one supported device. Pressing the "Build" button will bring up a save dialogue where you must chose a file name for your particular package. Don't worry about specifying the ".tar.gz" extension Heimdall Frontend will take care of this automatically. Once you've chosen a file name Heimdall Frontend will begin the process of building the firmware package. In doing so a valid firmware.xml file will be generated from the information entered. All files will be archived in a single TAR file then the TAR archive will be compressed via gzip compression. Compression will take a little while but you will see progress bars so you know the application hasn't hung. When the progress bars disappear you're finished making your package. Congratulations! You're now ready to redistribute your firmware package online or by any means you see fit. # Appendix A - firmware.xml The following details a part of the Heimdall Firmware Package format. This is only relevant to developers or advanced users who wish to create Heimdall Firmware Packages outside of Heimdall Frontend or in some way integrate support for the format in their own software. All Heimdall Firmware Packages must contain a file called firmware.xml. This file stores flash information and meta-data for the package as well as information about other files contained within the package. The format is fairly straight-forward so it won't be explained in great detail. Nonetheless the following is an example of a valid firmware.xml file. Test Firmware 1.1 Android 2.3.4 Benjamin Dobell Hedonism Bot https://www.glassechidna.com.au/ https://www.glassechidna.com.au/donate/ Samsung GT-I9000 Galaxy S Samsung GT-I9000T Galaxy S (Telstra) Samsung GT-I9000M Vibrant Nl3276-I9000 s1_odin_20100512.pit 0 0 0 gq3276-boot.bin 24 Uh3276-cache.rfs 22 em3276-factoryfs.rfs 11 fl3276-modem.bin 21 Xd3276-param.lfs 3 if3276-Sbl.bin 6 cr3276-zImage New lines need not be included and the order in which elements are specified does not need to match that of the above example. One and only one element must be included. The element must also have a version attribute specified. The version must be parsable as an integer and indicates what version of the Heimdall Firmware Package specification the package adheres to. All data is stored as strings, however a 's element must be parsable as an integer. The value represents the partition ID (according to the specified PIT file) that the file should be flashed to. A 's and elements must also be parsable as an integer. However, as they represent boolean values, a value of zero ("0") means false (or disabled) where as a non-zero value (typically "1") means true (or enabled). File names are specified relative to the TAR archive in which firmware.xml and all other files are to be stored. Heimdall Firmware Packages do not support directories or links, as such file names should only be a name and not a path. and are the only optional elements, all other elements must be included. Heimdall-v2.1.0/doc/Heimdall-Frontend-flash-instructions.md000066400000000000000000000113601464356164000236510ustar00rootroot00000000000000Heimdall (c) 2010-2017 Benjamin Dobell, Glass Echidna # Performing a Custom Flash with Heimdall Frontend This is the advanced means of flashing firmware to your device. If you're not an advanced user or a developer, in the event that a Heimdall Firmware Package doesn't exist for the particular firmware (or files) that you wish to flash, then I strongly recommend you get in touch with developer of the firmware (or files) and politely ask them to create a Heimdall Firmware Package for you. In doing so then you don't have to worry about making mistakes due to inexperience. If you're looking to customise an existing Heimdall Firmware Package then follow steps 1-8 of "Flashing Heimdall Firmware Package with Heimdall Frontend" then start from below with step 5. 1. Fully charge your device (use the wall charger as it's faster). 2. Download a decrypted device ROM or a Heimdall Firmware Package and extract everything to the one directory. 3. If the ROM is not a Heimdall Firmware Package it may instead be provided as multiple archives (nested or otherwise), extract them all to the same location. NOTE: If you want to use the CSC then extract it last. 3. Open the Heimdall Frontend (heimdall-frontend) application. 4. Select the "Flash" tab. From the "Flash" tab you're able to completely customise a flash. 5. Before you can chose which partitions you want to flash with particular files you MUST first select a PIT file. To do this click the "Browse" button in the "PIT" section. This will open a dialogue allowing you to navigate to and select a valid PIT (.pit) file. If you do not already have a valid PIT file stored on your computer you can download your device's PIT file from the "Utilities" tab. 6. If a valid PIT file has been selected then "Add" button below the "Partitions (Files)" list-box will become enabled. Press this button to add a partition to your flash. 7. When you first add a partition you will see the "Partition Name" and "Partition ID" be populated with information. Use the "Partition Name" drop down to select which partition you wish to flash. "Partition ID" will automatically update and is never directly editable. 8. You must then select a file to flash to the partition that you just specified using the "Browse" button under the "File / Partition". You will not be able to flash, create a firmware package or add another partition until you have selected a file. However, you're still able to press the "Remove" button if you've decided not to flash the partition you've just specified. 9. When you've specified a file name then you'll be able to see the updated information in the partition list to the right. You can select any partition from this list and customise it as you see fit. You can also remove a partition from the list by selecting it and clicking the "Remove" button. Removing a partition from the list doesn't remove it from your device, it simply means it will not be flashed. 10. Repeat steps 7-9 as often as needed to specify all the partitions/files that you wish to flash. 11. Now you can chose whether you would like to repartition your device as well as whether you would like to prevent the device rebooting once a flash has been completed. These options can be enabled or disabled by toggling the "Repartition" and "No Reboot" check-boxes. In the general case you will only need to enable repartition if you wish to change the PIT file on your device. Keep in mind that repartitioning will wipe your device! The "No Reboot" option is rarely required. It's mostly in place so you can manually boot straight into recovery mode after a flash (rather than booting up normally). 12. If you've added at least one partition to your flash (and selected a file for that partition) then the "Start" button will be enabled. Press the "Start" button to begin the flashing process. You may notice that the "Create Package" tab becomes available at the whenever the "Start" button becomes available. From this tab you're able to create a reusable, redistributable Heimdall Firmware Package with the files and partitions you just selected. See "How to Create a Heimdall Firmware Package" for details. 13. Heimdall Frontend will display the progress and inform you when the flash is complete. If something went wrong i.e. your device wasn't detected because it wasn't in download mode, then the status section will let you know the cause of the problem. Heimdall-v2.1.0/doc/Heimdall-flash-instructions.md000066400000000000000000000036601464356164000221000ustar00rootroot00000000000000Heimdall (c) 2010-2017 Benjamin Dobell, Glass Echidna # Flashing Firmware from Command Line 1. Fully charge your phone (use the wall charger as it's faster). 2. Download a decrypted device ROM or a Heimdall Firmware Package and extract everything to the one directory. 3. If the ROM is not a Heimdall Firmware Package it may instead be provided as multiple archives (nested or otherwise), extract them all to the same location. NOTE: If you want to use the CSC then extract it last. If you're asked to overwrite files then do so. 4. Put your Galaxy S device into download mode and plug it in.. 5. Open a terminal and navigate to the directory where you extracted the ROM/firmware files. 6. Type the following to list all the functionality Heimdall supports: heimdall help 7. Before flashing, you must first know the names of the partitions you wish to flash. These can be obtained by executing: heimdall print-pit --no-reboot The inclusion of --no-reboot ensures the phone will not reboot after PIT file has been downloaded and displayed. After executing a command with the --no-reboot argument, the next command should include the --resume argument. NOTE: You can still safely reboot your phone manually (with the power button) after executing --no-reboot commands. 8. Use the help and print-pit output to construct a command with all the file you want to flash. Here is an example that does a full flash and repartition on a GT-I9000: heimdall flash --repartition --resume --pit s1_odin_20100512.pit --FACTORYFS factoryfs.rfs --CACHE cache.rfs --DBDATA dbdata.rfs --IBL+PBL boot.bin --SBL Sbl.bin --PARAM param.lfs --KERNEL zImage --MODEM modem.bin 9. Heimdall will display the progress as it flashes so that you know things are working as they should. Heimdall-v2.1.0/heimdall-frontend/000077500000000000000000000000001464356164000170245ustar00rootroot00000000000000Heimdall-v2.1.0/heimdall-frontend/CMakeLists.txt000066400000000000000000000043051464356164000215660ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(heimdall-frontend) set(LIBPIT_INCLUDE_DIRS ../libpit/source) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) # moc files are generated in build (current) directory find_package(Qt5Widgets REQUIRED) find_package(ZLIB REQUIRED) if(APPLE) set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lobjc -framework IOKit -framework CoreFoundation") endif() if(MINGW) set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -static") endif(MINGW) include_directories(${LIBPIT_INCLUDE_DIRS}) set(HEIMDALL_FRONTEND_SOURCE_FILES source/aboutform.cpp source/Alerts.cpp source/FirmwareInfo.cpp source/main.cpp source/mainwindow.cpp source/PackageData.cpp source/Packaging.cpp) qt5_wrap_ui(HEIMDALL_FRONTEND_FORMS mainwindow.ui aboutform.ui) qt5_add_resources(HEIMDALL_FRONTEND_RESOURCES mainwindow.qrc) add_executable(heimdall-frontend WIN32 MACOSX_BUNDLE ${HEIMDALL_FRONTEND_SOURCE_FILES} ${HEIMDALL_FRONTEND_FORMS} ${HEIMDALL_FRONTEND_RESOURCES}) target_compile_features(heimdall-frontend PRIVATE cxx_std_11) include(CheckSymbolExists) # # Large file support on UN*X, a/k/a LFS. # # On Windows, we require _fseeki64() and _ftelli64(). Visual Studio # has had supported them since Visual Studio 2005/MSVCR80. # if(NOT WIN32) include(FindLFS) if(LFS_FOUND) # # Add the required #defines. # add_definitions(${LFS_DEFINITIONS}) endif() # # Check for fseeko as well. # include(FindFseeko) if(FSEEKO_FOUND) set(HAVE_FSEEKO ON) # # Add the required #defines. # add_definitions(${FSEEKO_DEFINITIONS}) endif() endif() set_property(TARGET heimdall-frontend APPEND PROPERTY COMPILE_DEFINITIONS "QT_LARGEFILE_SUPPORT") target_link_libraries(heimdall-frontend pit) target_link_libraries(heimdall-frontend Qt5::Widgets) target_link_libraries(heimdall-frontend z) install (TARGETS heimdall-frontend RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} BUNDLE DESTINATION /Applications) Heimdall-v2.1.0/heimdall-frontend/aboutform.ui000066400000000000000000000615701464356164000213720ustar00rootroot00000000000000 AboutForm Qt::ApplicationModal 0 0 581 491 0 0 581 491 581 491 About Heimdall Frontend 250 460 75 21 OK 10 10 561 441 0 0 Qt::ScrollBarAlwaysOff true 0 -701 542 1140 0 0 16777215 16777215 0 0 0 10 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "https://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:'MS Shell Dlg 2'; font-size:10pt; 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-weight:600;">Heimdall Frontend</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%HEIMDALL-VERSION%</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Copyright © 2010-2017 Benjamin Dobell, Glass Echidna</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;"></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-weight:600;">Heimdall (command line)</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">%HEIMDALL-VERSION%Copyright © 2010-2017 Benjamin Dobell, Glass Echidna</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Copyright © 2021-2024 Henrik Grimler</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;"></p></body></html> Qt::RichText true Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true false Qt::Vertical 20 15 10 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "https://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:'Lucida Grande'; font-size:13pt; 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:10pt; font-weight:600;">Glass Echidna Homepage:</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="https://www.glassechidna.com.au/"><span style=" font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;">https://www.glassechidna.com.au/</span></a></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:10pt; text-decoration: underline; color:#0000ff;"></p></body></html> Qt::RichText true Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true true 10 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "https://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:'Lucida Grande'; font-size:13pt; 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:10pt; font-weight:600;">Heimdall Homepage:</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="https://www.glassechidna.com.au/products/heimdall/"><span style=" font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;">https://www.glassechidna.com.au/products/heimdall/</span></a></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:10pt; text-decoration: underline; color:#0000ff;"></p></body></html> Qt::RichText true Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true true 9 true false QFrame::NoFrame QFrame::Plain <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "https://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:'Lucida Grande'; font-size:13pt; 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:10pt; font-weight:600;">Heimdall and Heimdall Frontend are licensed under the following license:</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:10pt;"></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:10pt;">Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &quot;Software&quot;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</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:10pt;"></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:10pt;">The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</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:10pt;"></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:10pt;">THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</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:10pt;"></p></body></html> Qt::RichText true Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true 0 2 false Qt::Vertical 20 15 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "https://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:'Lucida Grande'; font-size:13pt; 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:10pt; font-weight:600;">Heimdall Frontend's user interface is powered by the Qt Framework:</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:10pt;"></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:10pt;">Copyright © 2008-2011 Nokia Corporation</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:10pt;">Qt is licensed under the </span><a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"><span style=" font-family:'MS Shell Dlg 2'; font-size:10pt; text-decoration: underline; color:#0000ff;">Lesser General Public License v2.1.</span></a></p></body></html> Qt::RichText true true Qt::Vertical 20 15 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "https://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:'Lucida Grande'; font-size:13pt; 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:10pt; font-weight:600;">Heimdall Frontend utilises zlib for package compression:</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:10pt;"></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:10pt;">zlib is licensed under the following license:</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:10pt;">Copyright © 1995-2010 Jean-loup Gailly and Mark Adler</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:10pt;"></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:10pt;">This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.</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:10pt;"></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:10pt;">Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:</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:10pt;"></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:10pt;">1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.</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:10pt;"></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:10pt;">2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.</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:10pt;"></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:10pt;">3. This notice may not be removed or altered from any source distribution.</span></p></body></html> Qt::RichText true false Qt::Vertical 20 15 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "https://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:'MS Shell Dlg 2'; font-size:8.25pt; 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-size:10pt; font-weight:600;">Heimdall (command line) utilises libusb for all USB communication:</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-size:10pt;"></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-size:10pt;">libusbx is licensed under the </span><a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">Lesser General Public License v2.1.</span></a></p></body></html> Qt::RichText true true okPushButton clicked() AboutForm close() 287 470 290 245 Heimdall-v2.1.0/heimdall-frontend/mainwindow.qrc000066400000000000000000000000631464356164000217060ustar00rootroot00000000000000 Heimdall-v2.1.0/heimdall-frontend/mainwindow.ui000066400000000000000000001517111464356164000215450ustar00rootroot00000000000000 MainWindow 0 0 788 525 0 0 788 525 788 525 Heimdall Frontend QTabWidget::Rounded true 5 0 780 501 0 0 780 0 780 780 0 false Load Package 510 10 251 331 Package Files 10 30 231 291 340 80 161 61 Platform false 10 30 141 21 true 10 220 491 241 Supported Devices 10 30 471 201 false 520 360 241 21 Repartition Recommended false 550 420 171 31 Qt::NoFocus Load / Customise 10 80 211 61 Firmware Name false 10 30 191 21 true 10 10 491 61 Heimdall Firmware Package false 10 30 391 21 true true 410 30 71 23 Qt::NoFocus Browse 230 80 101 61 Version false 10 30 81 21 true 10 150 491 61 Developer(s) false 10 30 281 21 true false 410 30 71 23 Qt::NoFocus Donate false 300 30 101 23 Qt::NoFocus Homepage false 520 390 241 21 No Reboot Recommended Flash 10 290 511 170 Status false 280 130 221 31 0 true 10 30 491 81 false true 10 130 261 21 Qt::RightToLeft Ready Qt::PlainText Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft 10 10 751 281 Options 10 20 391 91 PIT false 10 30 261 21 true true 280 30 71 23 Qt::NoFocus Browse false false false true 360 30 21 23 75 true You can retrieve/download your device's PIT file from the Utilities tab. QFrame::Panel QFrame::Raised 2 0 ? Qt::AlignCenter false 10 60 131 21 Repartitioning will wipe all data for your phone and install the selected PIT file. Repartition 10 120 391 151 Partition Details false 140 30 241 22 10 30 121 16 Partition Name 10 60 121 16 Partition ID 10 80 371 61 File false 10 30 271 21 true false 290 30 71 23 Browse false 140 60 241 21 true 410 20 331 251 Partitions (Files) false 10 220 81 23 Add false 10 20 311 191 false 240 220 81 23 Remove 100 220 21 23 75 true Use the "Add" button to add additional files to be flashed. QFrame::Panel QFrame::Raised 2 0 ? Qt::AlignCenter 530 290 231 171 Session false 10 30 211 21 Can be enabled to prevent your device rebooting after the flash finishes. No Reboot false 10 60 211 21 Resume flash action, to be used after "No Reboot" was used. Resume (use after "No Reboot") false 10 90 211 21 If enabled then Heimdall waits until a compatible device is connected and then flashes. Wait for device false 50 120 111 31 Start 170 120 21 23 75 true The "Start" button will remain inactive until at least one partition/file is added. QFrame::Panel QFrame::Raised 2 0 ? Qt::AlignCenter true Create Package 0 230 471 231 Supported Devices 10 30 451 161 false 320 200 141 23 Qt::NoFocus Remove Device 240 10 151 61 Firmware Version true 10 30 131 21 false 430 10 171 61 Platform Name true 10 30 151 21 false 10 10 221 61 Firmware Name true 10 30 201 21 false 10 80 751 141 Developers true 560 20 181 81 310 20 241 101 Developer Info 10 30 51 16 Name true 70 30 161 21 false false 130 60 101 23 Qt::NoFocus Add false 640 110 101 23 Qt::NoFocus Remove 10 20 291 101 URLs (Optional) 10 60 81 16 Donate 10 30 81 16 Homepage true 100 30 181 21 false true 100 60 181 21 false 610 10 151 61 Platform Version true 10 30 131 21 false 480 240 291 151 Device Info 10 60 111 16 Name true 130 60 151 21 false 10 30 111 16 Manufacturer true 130 30 151 21 false 10 90 111 16 Product Code true 130 90 151 21 false false 160 120 121 23 Qt::NoFocus Add Device false 580 420 121 31 Qt::NoFocus Build Utilities 10 70 461 141 Download PIT 10 30 441 71 Destination File false 10 30 311 21 true true 340 30 91 23 Qt::NoFocus Save As... false 320 110 101 23 Qt::NoFocus Download 430 110 21 23 75 true Download and save a device's PIT file. QFrame::Panel QFrame::Raised 2 0 ? Qt::AlignCenter 10 220 751 241 Output true 10 30 731 201 false true 10 10 291 61 Detect Device 170 30 81 23 Qt::NoFocus Detect false 10 30 151 21 Device Detected true true 260 30 21 23 75 true Detect whether or not a device is connected in download mode. QFrame::Panel QFrame::Raised 2 0 ? Qt::AlignCenter 480 10 291 201 Print PIT 160 170 81 23 Qt::NoFocus Print 250 170 21 23 75 true Print the contents of a PIT file in a human readable fashion. QFrame::Panel QFrame::Raised 2 0 ? Qt::AlignCenter true 20 30 261 21 Device true true true 20 60 261 21 Local File true false 10 90 271 71 PIT File false 10 30 171 21 true false 190 30 71 23 Qt::NoFocus Browse 310 10 161 61 Close PC Screen 40 30 81 23 Qt::NoFocus Close 130 30 21 23 75 true Close the "device <--> PC" screen displayed on a device. QFrame::Panel QFrame::Raised 2 0 ? Qt::AlignCenter 0 0 788 24 Help Advanced Help About Heimdall Package Creation true Verbose Output true Resume Connection functionTabWidget firmwarePackageLineEdit browseFirmwarePackageButton firmwareNameLineEdit versionLineEdit platformLineEdit developerNamesLineEdit developerHomepageButton developerDonateButton supportedDevicesListWidget includedFilesListWidget repartitionRadioButton loadFirmwareButton pitLineEdit pitBrowseButton partitionNameComboBox partitionIdLineEdit partitionFileLineEdit partitionFileBrowseButton outputPlainTextEdit createFirmwareNameLineEdit createFirmwareVersionLineEdit createPlatformNameLineEdit createPlatformVersionLineEdit createHomepageLineEdit createDonateLineEdit createDeveloperNameLineEdit addDeveloperButton createDevelopersListWidget removeDeveloperButton createDevicesListWidget removeDeviceButton deviceManufacturerLineEdit deviceNameLineEdit deviceProductCodeLineEdit addDeviceButton buildPackageButton Heimdall-v2.1.0/heimdall-frontend/source/000077500000000000000000000000001464356164000203245ustar00rootroot00000000000000Heimdall-v2.1.0/heimdall-frontend/source/Alerts.cpp000066400000000000000000000031461464356164000222660ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Qt #include // Heimdall Frontend #include "Alerts.h" using namespace HeimdallFrontend; void Alerts::DisplayError(const QString& errorMessage) { QMessageBox messageBox; messageBox.setModal(true); messageBox.setText(errorMessage); messageBox.setIcon(QMessageBox::Critical); messageBox.exec(); } void Alerts::DisplayWarning(const QString& warningMessage) { QMessageBox messageBox; messageBox.setModal(true); messageBox.setText(warningMessage); messageBox.setIcon(QMessageBox::Warning); messageBox.exec(); } Heimdall-v2.1.0/heimdall-frontend/source/Alerts.h000066400000000000000000000025131464356164000217300ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef ALERTS_H #define ALERTS_H // Qt #include namespace HeimdallFrontend { class Alerts { public: static void DisplayError(const QString& errorMessage); static void DisplayWarning(const QString& warningMessage); }; } #endif Heimdall-v2.1.0/heimdall-frontend/source/FirmwareInfo.cpp000066400000000000000000000410131464356164000234170ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Qt #include "QRegExp" // Heimdall Frontend #include "Alerts.h" #include "FirmwareInfo.h" #include "Packaging.h" using namespace HeimdallFrontend; DeviceInfo::DeviceInfo() { } DeviceInfo::DeviceInfo(const QString& manufacturer, const QString& product, const QString& name) { this->manufacturer = manufacturer; this->product = product; this->name = name; } bool DeviceInfo::ParseXml(QXmlStreamReader& xml) { bool foundManufacturer = false; bool foundProduct = false; bool foundName = false; while (!xml.atEnd()) { QXmlStreamReader::TokenType nextToken = xml.readNext(); if (nextToken == QXmlStreamReader::StartElement) { if (xml.name() == "manufacturer") { if (foundManufacturer) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundManufacturer = true; manufacturer = xml.readElementText(); } else if (xml.name() == "product") { if (foundProduct) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundProduct = true; product = xml.readElementText(); } else if (xml.name() == "name") { if (foundName) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundName = true; name = xml.readElementText(); } else { Alerts::DisplayError(QString("<%1> is not a valid child of .").arg(xml.name().toString())); return (false); } } else if (nextToken == QXmlStreamReader::EndElement) { if (xml.name() == "device") { if (foundManufacturer && foundProduct && foundName) { return (true); } else { Alerts::DisplayError("Required elements are missing from ."); return (false); } } } else { if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace())) { Alerts::DisplayError("Unexpected token found in ."); return (false); } } } return (false); } void DeviceInfo::WriteXml(QXmlStreamWriter& xml) const { xml.writeStartElement("device"); xml.writeStartElement("manufacturer"); xml.writeCharacters(manufacturer); xml.writeEndElement(); xml.writeStartElement("product"); xml.writeCharacters(product); xml.writeEndElement(); xml.writeStartElement("name"); xml.writeCharacters(name); xml.writeEndElement(); xml.writeEndElement(); } PlatformInfo::PlatformInfo() { } void PlatformInfo::Clear(void) { name.clear(); version.clear(); } bool PlatformInfo::IsCleared(void) const { return (name.isEmpty() && version.isEmpty()); } bool PlatformInfo::ParseXml(QXmlStreamReader& xml) { Clear(); bool foundName = false; bool foundVersion = false; while (!xml.atEnd()) { QXmlStreamReader::TokenType nextToken = xml.readNext(); if (nextToken == QXmlStreamReader::StartElement) { if (xml.name() == "name") { if (foundName) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundName = true; name = xml.readElementText(); } else if (xml.name() == "version") { if (foundVersion) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundVersion = true; version = xml.readElementText(); } else { Alerts::DisplayError(QString("<%1> is not a valid child of .").arg(xml.name().toString())); return (false); } } else if (nextToken == QXmlStreamReader::EndElement) { if (xml.name() == "platform") { if (foundName && foundVersion) { return (true); } else { Alerts::DisplayError("Required elements are missing from ."); return (false); } } } else { if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace())) { Alerts::DisplayError("Unexpected token found in ."); return (false); } } } return (false); } void PlatformInfo::WriteXml(QXmlStreamWriter& xml) const { xml.writeStartElement("platform"); xml.writeStartElement("name"); xml.writeCharacters(name); xml.writeEndElement(); xml.writeStartElement("version"); xml.writeCharacters(version); xml.writeEndElement(); xml.writeEndElement(); } FileInfo::FileInfo() { } FileInfo::FileInfo(unsigned int partitionId, const QString& filename) { this->partitionId = partitionId; this->filename = filename; } bool FileInfo::ParseXml(QXmlStreamReader& xml) { bool foundId = false; bool foundFilename = false; while (!xml.atEnd()) { QXmlStreamReader::TokenType nextToken = xml.readNext(); if (nextToken == QXmlStreamReader::StartElement) { if (xml.name() == "id") { if (foundId) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundId = true; partitionId = xml.readElementText().toInt(); } else if (xml.name() == "filename") { if (foundFilename) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundFilename = true; filename = xml.readElementText(); } else { Alerts::DisplayError(QString("<%1> is not a valid child of .").arg(xml.name().toString())); return (false); } } else if (nextToken == QXmlStreamReader::EndElement) { if (xml.name() == "file") { if (foundId && foundFilename) { return (true); } else { Alerts::DisplayError("Required elements are missing from ."); return (false); } } } else { if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace())) { Alerts::DisplayError("Unexpected token found in ."); return (false); } } } return (false); } void FileInfo::WriteXml(QXmlStreamWriter& xml, const QString& filename) const { xml.writeStartElement("file"); xml.writeStartElement("id"); xml.writeCharacters(QString::number(partitionId)); xml.writeEndElement(); xml.writeStartElement("filename"); xml.writeCharacters(filename); xml.writeEndElement(); xml.writeEndElement(); } FirmwareInfo::FirmwareInfo() { repartition = false; noReboot = false; } void FirmwareInfo::Clear(void) { name = ""; version = ""; platformInfo.Clear(); developers.clear(); url.clear(); donateUrl.clear(); deviceInfos.clear(); pitFilename.clear(); repartition = false; noReboot = false; fileInfos.clear(); } bool FirmwareInfo::IsCleared(void) const { return (name.isEmpty() && version.isEmpty() && platformInfo.IsCleared() && developers.isEmpty() && url.isEmpty() && url.isEmpty() && donateUrl.isEmpty() && deviceInfos.isEmpty() && pitFilename.isEmpty() && !repartition && !noReboot && fileInfos.isEmpty()); } bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) { Clear(); bool foundName = false; bool foundVersion = false; bool foundPlatform = false; bool foundDevelopers = false; bool foundUrl = false; bool foundDonateUrl = false; bool foundDevices = false; bool foundPit = false; bool foundRepartition = false; bool foundNoReboot = false; bool foundFiles = false; if (!xml.readNextStartElement()) { Alerts::DisplayError("Failed to find element."); return (false); } if (xml.name() != "firmware") { Alerts::DisplayError(QString("Expected element but found <%1>.").arg(xml.name().toString())); return (false); } QString formatVersionString; formatVersionString += xml.attributes().value("version"); if (formatVersionString.isEmpty()) { Alerts::DisplayError(" is missing the version attribute."); return (false); } bool parsedVersion = false; int formatVersion = formatVersionString.toInt(&parsedVersion); if (!parsedVersion) { Alerts::DisplayError(" contains a malformed version."); return (false); } if (formatVersion > kVersion) { Alerts::DisplayError("Package is for a newer version of Heimdall Frontend.\nPlease download the latest version of Heimdall Frontend."); return (false); } while (!xml.atEnd()) { QXmlStreamReader::TokenType nextToken = xml.readNext(); if (nextToken == QXmlStreamReader::StartElement) { if (xml.name() == "name") { if (foundName) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundName = true; name = xml.readElementText(); } else if (xml.name() == "version") { if (foundVersion) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundVersion = true; version = xml.readElementText(); } else if (xml.name() == "platform") { if (foundPlatform) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundPlatform = true; if (!platformInfo.ParseXml(xml)) return (false); } else if (xml.name() == "developers") { if (foundDevelopers) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundDevelopers = true; while (!xml.atEnd()) { nextToken = xml.readNext(); if (nextToken == QXmlStreamReader::StartElement) { if (xml.name() == "name") { developers.append(xml.readElementText()); } else { Alerts::DisplayError(QString("<%1> is not a valid child of .").arg(xml.name().toString())); return (false); } } else if (nextToken == QXmlStreamReader::EndElement) { if (xml.name() == "developers") break; } else { if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace())) { Alerts::DisplayError("Unexpected token found in ."); return (false); } } } } else if (xml.name() == "url") { if (foundUrl) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundUrl = true; url = xml.readElementText(); } else if (xml.name() == "donateurl") { if (foundDonateUrl) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundDonateUrl = true; donateUrl = xml.readElementText(); } else if (xml.name() == "devices") { if (foundDevices) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundDevices = true; while (!xml.atEnd()) { nextToken = xml.readNext(); if (nextToken == QXmlStreamReader::StartElement) { if (xml.name() == "device") { DeviceInfo deviceInfo; if (!deviceInfo.ParseXml(xml)) return (false); deviceInfos.append(deviceInfo); } else { Alerts::DisplayError(QString("<%1> is not a valid child of .").arg(xml.name().toString())); return (false); } } else if (nextToken == QXmlStreamReader::EndElement) { if (xml.name() == "devices") break; } else { if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace())) { Alerts::DisplayError("Unexpected token found in ."); return (false); } } } } else if (xml.name() == "pit") { if (foundPit) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundPit = true; pitFilename = xml.readElementText(); } else if (xml.name() == "repartition") { if (foundRepartition) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundRepartition = true; repartition = (xml.readElementText().toInt() != 0); } else if (xml.name() == "noreboot") { if (foundNoReboot) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundNoReboot = true; noReboot = (xml.readElementText().toInt() != 0); } else if (xml.name() == "files") { if (foundFiles) { Alerts::DisplayError("Found multiple elements in ."); return (false); } foundFiles = true; while (!xml.atEnd()) { nextToken = xml.readNext(); if (nextToken == QXmlStreamReader::StartElement) { if (xml.name() == "file") { FileInfo fileInfo; if (!fileInfo.ParseXml(xml)) return (false); fileInfos.append(fileInfo); } else { Alerts::DisplayError(QString("<%1> is not a valid child of .").arg(xml.name().toString())); return (false); } } else if (nextToken == QXmlStreamReader::EndElement) { if (xml.name() == "files") break; } else { if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace())) { Alerts::DisplayError("Unexpected token found in ."); return (false); } } } } else { Alerts::DisplayError(QString("<%1> is not a valid child of .").arg(xml.name().toString())); return (false); } } else if (nextToken == QXmlStreamReader::EndElement) { if (xml.name() == "firmware") { if (!(foundName && foundVersion && foundPlatform && foundDevelopers && foundDevices && foundPit && foundRepartition && foundNoReboot && foundFiles)) { Alerts::DisplayError("Required elements are missing from ."); return (false); } else { break; } } } else { if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace())) { Alerts::DisplayError("Unexpected token found in ."); return (false); } } } // Read whitespaces at the end of the file (if there are any) xml.readNext(); if (!xml.atEnd()) { Alerts::DisplayError("Found data after ."); return (false); } return (true); } void FirmwareInfo::WriteXml(QXmlStreamWriter& xml) const { xml.writeStartDocument(); xml.writeStartElement("firmware"); xml.writeAttribute("version", QString::number(FirmwareInfo::kVersion)); xml.writeStartElement("name"); xml.writeCharacters(name); xml.writeEndElement(); xml.writeStartElement("version"); xml.writeCharacters(version); xml.writeEndElement(); platformInfo.WriteXml(xml); xml.writeStartElement("developers"); for (int i = 0; i < developers.length(); i++) { xml.writeStartElement("name"); xml.writeCharacters(developers[i]); xml.writeEndElement(); } xml.writeEndElement(); if (!url.isEmpty()) { xml.writeStartElement("url"); xml.writeCharacters(url); xml.writeEndElement(); } if (!donateUrl.isEmpty()) { xml.writeStartElement("donateurl"); xml.writeCharacters(donateUrl); xml.writeEndElement(); } xml.writeStartElement("devices"); for (int i = 0; i < deviceInfos.length(); i++) deviceInfos[i].WriteXml(xml); xml.writeEndElement(); xml.writeStartElement("pit"); int lastSlash = pitFilename.lastIndexOf('/'); if (lastSlash < 0) lastSlash = pitFilename.lastIndexOf('\\'); xml.writeCharacters(pitFilename.mid(lastSlash + 1)); xml.writeEndElement(); xml.writeStartElement("repartition"); xml.writeCharacters((repartition) ? "1" : "0"); xml.writeEndElement(); xml.writeStartElement("noreboot"); xml.writeCharacters((noReboot) ? "1" : "0"); xml.writeEndElement(); xml.writeStartElement("files"); for (int i = 0; i < fileInfos.length(); i++) { fileInfos[i].WriteXml(xml, Packaging::ClashlessFilename(fileInfos, i)); } xml.writeEndElement(); xml.writeEndElement(); xml.writeEndDocument(); } Heimdall-v2.1.0/heimdall-frontend/source/FirmwareInfo.h000066400000000000000000000125731464356164000230750ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef FIRMWAREINFO_H #define FIRMWAREINFO_H // Qt #include #include #include namespace HeimdallFrontend { class DeviceInfo { private: QString manufacturer; QString product; QString name; public: DeviceInfo(); DeviceInfo(const QString& manufacturer, const QString& product, const QString& name); bool ParseXml(QXmlStreamReader& xml); void WriteXml(QXmlStreamWriter& xml) const; const QString& GetManufacturer(void) const { return (manufacturer); } void SetManufacturer(const QString& manufacturer) { this->manufacturer = manufacturer; } const QString& GetProduct(void) const { return (product); } void SetProduct(const QString& product) { this->product = product; } const QString& GetName(void) const { return (name); } void SetName(const QString& name) { this->name = name; } }; class PlatformInfo { private: QString name; QString version; public: PlatformInfo(); void Clear(void); bool IsCleared(void) const; bool ParseXml(QXmlStreamReader& xml); void WriteXml(QXmlStreamWriter& xml) const; const QString& GetName(void) const { return (name); } void SetName(const QString& name) { this->name = name; } const QString& GetVersion(void) const { return (version); } void SetVersion(const QString& version) { this->version = version; } }; class FileInfo { private: unsigned int partitionId; QString filename; public: FileInfo(); FileInfo(unsigned int partitionId, const QString& filename); bool ParseXml(QXmlStreamReader& xml); void WriteXml(QXmlStreamWriter& xml, const QString& filename) const; unsigned int GetPartitionId(void) const { return (partitionId); } void SetPartitionId(unsigned int partitionId) { this->partitionId = partitionId; } const QString& GetFilename(void) const { return (filename); } void SetFilename(const QString& filename) { this->filename = filename; } }; class FirmwareInfo { public: enum { kVersion = 1 }; private: QString name; QString version; PlatformInfo platformInfo; QList developers; QString url; QString donateUrl; QList deviceInfos; QString pitFilename; bool repartition; bool noReboot; QList fileInfos; public: FirmwareInfo(); void Clear(void); bool IsCleared(void) const; bool ParseXml(QXmlStreamReader& xml); void WriteXml(QXmlStreamWriter& xml) const; const QString& GetName(void) const { return (name); } void SetName(const QString& name) { this->name = name; } const QString& GetVersion(void) const { return (version); } void SetVersion(const QString& version) { this->version = version; } const PlatformInfo& GetPlatformInfo(void) const { return (platformInfo); } PlatformInfo& GetPlatformInfo(void) { return (platformInfo); } const QList& GetDevelopers(void) const { return (developers); } QList& GetDevelopers(void) { return (developers); } const QString& GetUrl(void) const { return (url); } void SetUrl(const QString& url) { this->url = url; } const QString& GetDonateUrl(void) const { return (donateUrl); } void SetDonateUrl(const QString& donateUrl) { this->donateUrl = donateUrl; } const QList& GetDeviceInfos(void) const { return (deviceInfos); } QList& GetDeviceInfos(void) { return (deviceInfos); } const QString& GetPitFilename(void) const { return (pitFilename); } void SetPitFilename(const QString& pitFilename) { this->pitFilename = pitFilename; } bool GetRepartition(void) const { return (repartition); } void SetRepartition(bool repartition) { this->repartition = repartition; } bool GetNoReboot(void) const { return (noReboot); } void SetNoReboot(bool noReboot) { this->noReboot = noReboot; } const QList& GetFileInfos(void) const { return (fileInfos); } QList& GetFileInfos(void) { return (fileInfos); } }; } #endif Heimdall-v2.1.0/heimdall-frontend/source/PackageData.cpp000066400000000000000000000035221464356164000231570ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Heimdall Frontend #include "Alerts.h" #include "PackageData.h" using namespace HeimdallFrontend; PackageData::PackageData() { } PackageData::~PackageData() { for (int i = 0; i < files.length(); i++) delete files[i]; } void PackageData::Clear(void) { firmwareInfo.Clear(); for (int i = 0; i < files.length(); i++) delete files[i]; files.clear(); } bool PackageData::ReadFirmwareInfo(QFile *file) { if (!file->open(QFile::ReadOnly)) { Alerts::DisplayError(QString("Failed to open file: \1%s").arg(file->fileName())); return (false); } QXmlStreamReader xml(file); bool success = firmwareInfo.ParseXml(xml); file->close(); return (success); } bool PackageData::IsCleared(void) const { return (firmwareInfo.IsCleared() && files.isEmpty()); } Heimdall-v2.1.0/heimdall-frontend/source/PackageData.h000066400000000000000000000036551464356164000226330ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef PACKAGEDATA_H #define PACKAGEDATA_H // Qt #include // Heimdall Frontend #include "FirmwareInfo.h" namespace HeimdallFrontend { class PackageData { private: FirmwareInfo firmwareInfo; QList files; public: PackageData(); ~PackageData(); void Clear(void); bool ReadFirmwareInfo(QFile *file); bool IsCleared(void) const; const FirmwareInfo& GetFirmwareInfo(void) const { return (firmwareInfo); } FirmwareInfo& GetFirmwareInfo(void) { return (firmwareInfo); } const QList& GetFiles(void) const { return (files); } QList& GetFiles(void) { return (files); } // Simply clears the files list, it does delete/close any files. void RemoveAllFiles(void) { files.clear(); } }; } #endif Heimdall-v2.1.0/heimdall-frontend/source/Packaging.cpp000066400000000000000000000551601464356164000227230ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifdef WIN32 #pragma warning(disable : 4996) #endif // C/C++ Standard Library #include // zlib #include "zlib.h" // Qt #include #include #include #include // Heimdall Frontend #include "Alerts.h" #include "Packaging.h" using namespace HeimdallFrontend; const qint64 Packaging::kMaxFileSize = 8589934592ll; const char *Packaging::ustarMagic = "ustar"; bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *packageData) { TarHeader tarHeader; if (!tarFile.open()) { Alerts::DisplayError(QString("Error opening temporary TAR archive:\n%1").arg(tarFile.fileName())); return (false); } bool previousEmpty = false; QProgressDialog progressDialog("Extracting files...", "Cancel", 0, tarFile.size()); progressDialog.setWindowModality(Qt::ApplicationModal); progressDialog.setWindowTitle("Heimdall Frontend"); while (!tarFile.atEnd()) { qint64 dataRead = tarFile.read(tarHeader.buffer, TarHeader::kBlockLength); if (dataRead != TarHeader::kBlockLength) { progressDialog.close(); Alerts::DisplayError("Package's TAR archive is malformed."); tarFile.close(); return (false); } progressDialog.setValue(tarFile.pos()); if (progressDialog.wasCanceled()) { tarFile.close(); progressDialog.close(); return (false); } //bool ustarFormat = strcmp(tarHeader.fields.magic, ustarMagic) == 0; bool empty = true; for (int i = 0; i < TarHeader::kBlockLength; i++) { if (tarHeader.buffer[i] != 0) { empty = false; break; } } if (empty) { if (previousEmpty) { // Two empty blocks in a row means we've reached the end of the archive. break; } } else { int checksum = 0; for (char *bufferIndex = tarHeader.buffer; bufferIndex < tarHeader.fields.checksum; bufferIndex++) checksum += static_cast(*bufferIndex); checksum += 8 * ' '; checksum += static_cast(tarHeader.fields.typeFlag); // Both the TAR and USTAR formats have terrible documentation, it's not clear if the following code is required. /*if (ustarFormat) { for (char *bufferIndex = tarHeader.fields.linkName; bufferIndex < tarHeader.fields.prefix + 155; bufferIndex++) checksum += static_cast(*bufferIndex); }*/ bool parsed = false; // The size field is not always null terminated, so we must create a copy and null terminate it for parsing. char fileSizeString[13]; memcpy(fileSizeString, tarHeader.fields.size, 12); fileSizeString[12] = '\0'; qulonglong fileSize = QString(fileSizeString).toULongLong(&parsed, 8); if (!parsed) { progressDialog.close(); Alerts::DisplayError("Tar header contained an invalid file size."); tarFile.close(); return (false); } if (fileSize > 0 && tarHeader.fields.typeFlag == '0') { // We're working with a file. QString filename = QString::fromUtf8(tarHeader.fields.name); QTemporaryFile *outputFile = new QTemporaryFile("XXXXXX-" + filename); packageData->GetFiles().append(outputFile); if (!outputFile->open()) { progressDialog.close(); Alerts::DisplayError(QString("Failed to open output file: \n%1").arg(outputFile->fileName())); tarFile.close(); return (false); } qulonglong dataRemaining = fileSize; char readBuffer[TarHeader::kBlockReadCount * TarHeader::kBlockLength]; // Copy the file contents from tarFile to outputFile while (dataRemaining > 0) { qint64 fileDataToRead = (dataRemaining < TarHeader::kBlockReadCount * TarHeader::kBlockLength) ? dataRemaining : TarHeader::kBlockReadCount * TarHeader::kBlockLength; qint64 dataRead = tarFile.read(readBuffer, fileDataToRead + (TarHeader::kBlockLength - fileDataToRead % TarHeader::kBlockLength) % TarHeader::kBlockLength); if (dataRead < fileDataToRead || dataRead % TarHeader::kBlockLength != 0) { progressDialog.close(); Alerts::DisplayError("Unexpected read error whilst extracting package files."); tarFile.close(); outputFile->close(); remove(outputFile->fileName().toStdString().c_str()); return (false); } outputFile->write(readBuffer, fileDataToRead); dataRemaining -= fileDataToRead; progressDialog.setValue(tarFile.pos()); if (progressDialog.wasCanceled()) { tarFile.close(); outputFile->close(); remove(outputFile->fileName().toStdString().c_str()); progressDialog.close(); return (false); } } outputFile->close(); } else { progressDialog.close(); Alerts::DisplayError("Heimdall packages shouldn't contain links or directories."); tarFile.close(); return (false); } } previousEmpty = empty; } progressDialog.close(); tarFile.close(); return (true); } bool Packaging::WriteTarEntry(const QString& filePath, QTemporaryFile *tarFile, const QString& entryFilename) { TarHeader tarHeader; memset(tarHeader.buffer, 0, TarHeader::kBlockLength); QFile file(filePath); if (!file.open(QFile::ReadOnly)) { Alerts::DisplayError(QString("Failed to open file: \n%1").arg(file.fileName())); return (false); } if (file.size() > Packaging::kMaxFileSize) { Alerts::DisplayError(QString("File is too large to be packaged:\n%1").arg(file.fileName())); return (false); } QFileInfo qtFileInfo(file); QByteArray utfFilename; utfFilename = entryFilename.toUtf8(); if (utfFilename.length() > 100) { Alerts::DisplayError(QString("File name is too long:\n%1").arg(qtFileInfo.fileName())); return (false); } strcpy(tarHeader.fields.name, utfFilename.constData()); unsigned int mode = 0; QFile::Permissions permissions = file.permissions(); // Other if (permissions.testFlag(QFile::ExeOther)) mode |= TarHeader::kModeOtherExecute; if (permissions.testFlag(QFile::WriteOther)) mode |= TarHeader::kModeOtherWrite; if (permissions.testFlag(QFile::ReadOther)) mode |= TarHeader::kModeOtherRead; // Group if (permissions.testFlag(QFile::ExeGroup)) mode |= TarHeader::kModeGroupExecute; if (permissions.testFlag(QFile::WriteGroup)) mode |= TarHeader::kModeGroupWrite; if (permissions.testFlag(QFile::ReadGroup)) mode |= TarHeader::kModeGroupRead; // Owner if (permissions.testFlag(QFile::ExeOwner)) mode |= TarHeader::kModeOwnerExecute; if (permissions.testFlag(QFile::WriteOwner)) mode |= TarHeader::kModeOwnerWrite; if (permissions.testFlag(QFile::ReadOwner)) mode |= TarHeader::kModeOwnerRead; sprintf(tarHeader.fields.mode, "%07o", mode); // Owner id uint id = qtFileInfo.ownerId(); if (id < 2097151) sprintf(tarHeader.fields.userId, "%07o", id); else sprintf(tarHeader.fields.userId, "%07o", 0); // Group id id = qtFileInfo.groupId(); if (id < 2097151) sprintf(tarHeader.fields.groupId, "%07o", id); else sprintf(tarHeader.fields.groupId, "%07o", 0); // Note: We don't support base-256 encoding. Support could be added later. sprintf(tarHeader.fields.size, "%011llo", file.size()); sprintf(tarHeader.fields.modifiedTime, "%u", qtFileInfo.lastModified().toTime_t()); // Regular File tarHeader.fields.typeFlag = '0'; // Calculate checksum int checksum = 0; memset(tarHeader.fields.checksum, ' ', 8); for (int i = 0; i < TarHeader::kTarHeaderLength; i++) checksum += static_cast(tarHeader.buffer[i]); sprintf(tarHeader.fields.checksum, "%07o", checksum); // Write the header to the TAR file. tarFile->write(tarHeader.buffer, TarHeader::kBlockLength); char buffer[TarHeader::kBlockWriteCount * TarHeader::kBlockLength]; qint64 offset = 0; while (offset < file.size()) { qint64 dataRead = file.read(buffer, TarHeader::kBlockWriteCount * TarHeader::kBlockLength); if (tarFile->write(buffer, dataRead) != dataRead) { Alerts::DisplayError("Failed to write data to the temporary TAR file."); return (false); } if (dataRead % TarHeader::kBlockLength != 0) { int remainingBlockLength = TarHeader::kBlockLength - dataRead % TarHeader::kBlockLength; memset(buffer, 0, remainingBlockLength); if (tarFile->write(buffer, remainingBlockLength) != remainingBlockLength) { Alerts::DisplayError("Failed to write data to the temporary TAR file."); return (false); } } offset += dataRead; } return (true); } bool Packaging::CreateTar(const FirmwareInfo& firmwareInfo, QTemporaryFile *tarFile) { const QList& fileInfos = firmwareInfo.GetFileInfos(); QProgressDialog progressDialog("Packaging files...", "Cancel", 0, fileInfos.length() + 2); progressDialog.setWindowModality(Qt::ApplicationModal); progressDialog.setWindowTitle("Heimdall Frontend"); QTemporaryFile firmwareXmlFile("XXXXXX-firmware.xml"); if (!firmwareXmlFile.open()) { progressDialog.close(); Alerts::DisplayError(QString("Failed to create temporary file: \n%1").arg(firmwareXmlFile.fileName())); return (false); } QXmlStreamWriter xml(&firmwareXmlFile); firmwareInfo.WriteXml(xml); firmwareXmlFile.close(); if (!tarFile->open()) { progressDialog.close(); Alerts::DisplayError(QString("Failed to open file: \n%1").arg(tarFile->fileName())); return (false); } for (int i = 0; i < fileInfos.length(); i++) { // If the file was already compressed we don't compress it again. bool skip = false; for (int j = 0; j < i; j++) { if (fileInfos[i].GetFilename() == fileInfos[j].GetFilename()) { skip = true; break; } } if (skip) { progressDialog.setValue(i); continue; } QString filename = ClashlessFilename(fileInfos, i); if (filename == "firmware.xml") { Alerts::DisplayError("You cannot name your partition files \"firmware.xml\".\nIt is a reserved name."); return (false); } if (!WriteTarEntry(fileInfos[i].GetFilename(), tarFile, filename)) { tarFile->resize(0); tarFile->close(); progressDialog.close(); return (false); } progressDialog.setValue(i); if (progressDialog.wasCanceled()) { tarFile->resize(0); tarFile->close(); progressDialog.close(); return (false); } } int lastSlash = firmwareInfo.GetPitFilename().lastIndexOf('/'); if (lastSlash < 0) lastSlash = firmwareInfo.GetPitFilename().lastIndexOf('\\'); QString pitFilename = ClashlessFilename(fileInfos, firmwareInfo.GetPitFilename().mid(lastSlash + 1)); if (pitFilename == "firmware.xml") { Alerts::DisplayError("You cannot name your PIT file \"firmware.xml\".\nIt is a reserved name."); return (false); } if (!WriteTarEntry(firmwareInfo.GetPitFilename(), tarFile, pitFilename)) { tarFile->resize(0); tarFile->close(); return (false); } progressDialog.setValue(progressDialog.value() + 1); if (progressDialog.wasCanceled()) { tarFile->resize(0); tarFile->close(); progressDialog.close(); return (false); } if (!WriteTarEntry(firmwareXmlFile.fileName(), tarFile, "firmware.xml")) { tarFile->resize(0); tarFile->close(); return (false); } progressDialog.setValue(progressDialog.value() + 1); progressDialog.close(); // Write two empty blocks to signify the end of the archive. char emptyEntry[TarHeader::kBlockLength]; memset(emptyEntry, 0, TarHeader::kBlockLength); tarFile->write(emptyEntry, TarHeader::kBlockLength); tarFile->write(emptyEntry, TarHeader::kBlockLength); tarFile->close(); return (true); } bool Packaging::ExtractPackage(const QString& packagePath, PackageData *packageData) { FILE *compressedPackageFile = fopen(packagePath.toStdString().c_str(), "rb"); if (!compressedPackageFile) { Alerts::DisplayError(QString("Failed to open package:\n%1").arg(packagePath)); return (false); } fseek(compressedPackageFile, 0, SEEK_END); quint64 compressedFileSize = ftell(compressedPackageFile); rewind(compressedPackageFile); gzFile packageFile = gzdopen(fileno(compressedPackageFile), "rb"); QTemporaryFile outputTar("XXXXXX.tar"); if (!outputTar.open()) { Alerts::DisplayError("Failed to open temporary TAR archive."); gzclose(packageFile); return (false); } char buffer[kExtractBufferLength]; int bytesRead; quint64 totalBytesRead = 0; QProgressDialog progressDialog("Decompressing package...", "Cancel", 0, compressedFileSize); progressDialog.setWindowModality(Qt::ApplicationModal); progressDialog.setWindowTitle("Heimdall Frontend"); do { bytesRead = gzread(packageFile, buffer, kExtractBufferLength); if (bytesRead == -1) { progressDialog.close(); Alerts::DisplayError("Error decompressing archive."); gzclose(packageFile); return (false); } outputTar.write(buffer, bytesRead); totalBytesRead += bytesRead; progressDialog.setValue(totalBytesRead); if (progressDialog.wasCanceled()) { gzclose(packageFile); progressDialog.close(); return (false); } } while (bytesRead > 0); progressDialog.close(); outputTar.close(); gzclose(packageFile); // Closes packageFile and compressedPackageFile if (!ExtractTar(outputTar, packageData)) return (false); // Find and read firmware.xml for (int i = 0; i < packageData->GetFiles().length(); i++) { QTemporaryFile *file = packageData->GetFiles()[i]; if (file->fileTemplate() == "XXXXXX-firmware.xml") { if (!packageData->ReadFirmwareInfo(file)) { packageData->Clear(); return (false); } return (true); } } Alerts::DisplayError("firmware.xml is missing from the package."); return (false); } bool Packaging::BuildPackage(const QString& packagePath, const FirmwareInfo& firmwareInfo) { FILE *compressedPackageFile = fopen(packagePath.toStdString().c_str(), "wb"); if (!compressedPackageFile) { Alerts::DisplayError(QString("Failed to create package:\n%1").arg(packagePath)); return (false); } QTemporaryFile tar("XXXXXX.tar"); if (!CreateTar(firmwareInfo, &tar)) { fclose(compressedPackageFile); remove(packagePath.toStdString().c_str()); return (false); } if (!tar.open()) { Alerts::DisplayError(QString("Failed to open temporary file: \n%1").arg(tar.fileName())); fclose(compressedPackageFile); remove(packagePath.toStdString().c_str()); return (false); } gzFile packageFile = gzdopen(fileno(compressedPackageFile), "wb"); char buffer[kCompressBufferLength]; qint64 totalBytesRead = 0; int bytesRead; QProgressDialog progressDialog("Compressing package...", "Cancel", 0, tar.size()); progressDialog.setWindowModality(Qt::ApplicationModal); progressDialog.setWindowTitle("Heimdall Frontend"); do { bytesRead = tar.read(buffer, kCompressBufferLength); if (bytesRead == -1) { progressDialog.close(); Alerts::DisplayError("Error reading temporary TAR file."); gzclose(packageFile); remove(packagePath.toStdString().c_str()); return (false); } if (gzwrite(packageFile, buffer, bytesRead) != bytesRead) { progressDialog.close(); Alerts::DisplayError("Error compressing package."); gzclose(packageFile); remove(packagePath.toStdString().c_str()); return (false); } totalBytesRead += bytesRead; progressDialog.setValue(totalBytesRead); if (progressDialog.wasCanceled()) { gzclose(packageFile); remove(packagePath.toStdString().c_str()); progressDialog.close(); return (false); } } while (bytesRead > 0); progressDialog.close(); gzclose(packageFile); // Closes packageFile and compressedPackageFile return (true); } QString Packaging::ClashlessFilename(const QList& fileInfos, int fileInfoIndex) { int lastSlash = fileInfos[fileInfoIndex].GetFilename().lastIndexOf('/'); if (lastSlash < 0) lastSlash = fileInfos[fileInfoIndex].GetFilename().lastIndexOf('\\'); QString filename = fileInfos[fileInfoIndex].GetFilename().mid(lastSlash + 1); unsigned int renameIndex = 0; // Check for name clashes for (int i = 0; i < fileInfoIndex; i++) { lastSlash = fileInfos[i].GetFilename().lastIndexOf('/'); if (lastSlash < 0) lastSlash = fileInfos[i].GetFilename().lastIndexOf('\\'); QString otherFilename = fileInfos[i].GetFilename().mid(lastSlash + 1); // If the filenames are the same, but the files themselves aren't the same (i.e. not the same path), then rename. if (filename == otherFilename && fileInfos[i].GetFilename() != fileInfos[fileInfoIndex].GetFilename()) renameIndex++; } if (renameIndex <= 0) { return (filename); } int lastPeriodIndex = filename.lastIndexOf(QChar('.')); QString shortFilename; QString fileType; if (lastPeriodIndex >= 0) { shortFilename = filename.left(lastPeriodIndex); fileType = filename.mid(lastPeriodIndex); } else { shortFilename = filename; } unsigned int renameIndexOffset = 0; bool validIndexOffset = true; // Before we append a rename index we must ensure it doesn't produce further collisions. for (int i = 0; i < fileInfos.length(); i++) { int lastSlash = fileInfos[i].GetFilename().lastIndexOf('/'); if (lastSlash < 0) lastSlash = fileInfos[i].GetFilename().lastIndexOf('\\'); QString otherFilename = fileInfos[i].GetFilename().mid(lastSlash + 1); if (otherFilename.length() > filename.length() + 1) { QString trimmedOtherFilename = otherFilename.left(shortFilename.length()); if (shortFilename == trimmedOtherFilename) { lastPeriodIndex = otherFilename.lastIndexOf(QChar('.')); QString shortOtherFilename; if (lastPeriodIndex >= 0) shortOtherFilename = otherFilename.left(lastPeriodIndex); else shortOtherFilename = otherFilename; QRegExp renameExp("-[0-9]+"); if (renameExp.lastIndexIn(shortOtherFilename) == shortFilename.length()) { unsigned int trailingInteger = shortOtherFilename.mid(shortFilename.length() + 1).toUInt(&validIndexOffset); if (!validIndexOffset) break; if (trailingInteger > renameIndexOffset) renameIndexOffset = trailingInteger; } } } } if (validIndexOffset) { // Ensure renaming won't involve integer overflow! if (renameIndex > static_cast(-1) - renameIndexOffset) validIndexOffset = false; } if (validIndexOffset) { shortFilename.append(QChar('-')); shortFilename.append(QString::number(renameIndexOffset + renameIndex)); return (shortFilename + fileType); } else { // Fallback behaviour... an absolutely terrible brute force implementation! QString filename; QString renamePrefix; for (;;) { renamePrefix.append(QChar('+')); for (unsigned int i = 0; i < static_cast(-1); i++) { filename = shortFilename + renamePrefix + QString::number(i) + fileType; bool valid = true; for (int i = 0; i < fileInfos.length(); i++) { int lastSlash = fileInfos[i].GetFilename().lastIndexOf('/'); if (lastSlash < 0) lastSlash = fileInfos[i].GetFilename().lastIndexOf('\\'); if (filename == fileInfos[i].GetFilename().mid(lastSlash + 1)) { valid = false; break; } } if (valid) return (filename); } } } } QString Packaging::ClashlessFilename(const QList& fileInfos, const QString& filename) { unsigned int renameIndex = 0; // Check for name clashes for (int i = 0; i < fileInfos.length(); i++) { int lastSlash = fileInfos[i].GetFilename().lastIndexOf('/'); if (lastSlash < 0) lastSlash = fileInfos[i].GetFilename().lastIndexOf('\\'); QString otherFilename = fileInfos[i].GetFilename().mid(lastSlash + 1); if (filename == otherFilename) renameIndex++; } if (renameIndex <= 0) { return (filename); } int lastPeriodIndex = filename.lastIndexOf(QChar('.')); QString shortFilename; QString fileType; if (lastPeriodIndex >= 0) { shortFilename = filename.left(lastPeriodIndex); fileType = filename.mid(lastPeriodIndex); } else { shortFilename = filename; } unsigned int renameIndexOffset = 0; bool validIndexOffset = true; // Before we append a rename index we must ensure it doesn't produce further collisions. for (int i = 0; i < fileInfos.length(); i++) { int lastSlash = fileInfos[i].GetFilename().lastIndexOf('/'); if (lastSlash < 0) lastSlash = fileInfos[i].GetFilename().lastIndexOf('\\'); QString otherFilename = fileInfos[i].GetFilename().mid(lastSlash + 1); if (otherFilename.length() > filename.length() + 1) { QString trimmedOtherFilename = otherFilename.left(filename.length()); if (filename == trimmedOtherFilename) { lastPeriodIndex = otherFilename.lastIndexOf(QChar('.')); QString shortOtherFilename; if (lastPeriodIndex >= 0) shortOtherFilename = otherFilename.left(lastPeriodIndex); else shortOtherFilename = otherFilename; QRegExp renameExp("-[0-9]+"); if (renameExp.lastIndexIn(shortOtherFilename) == shortFilename.length()) { unsigned int trailingInteger = shortOtherFilename.mid(shortFilename.length() + 1).toUInt(&validIndexOffset); if (!validIndexOffset) break; if (trailingInteger > renameIndexOffset) renameIndexOffset = trailingInteger; } } } } if (validIndexOffset) { // Ensure renaming won't involve integer overflow! if (renameIndex > static_cast(-1) - renameIndexOffset) validIndexOffset = false; } if (validIndexOffset) { shortFilename.append(QChar('-')); shortFilename.append(QString::number(renameIndexOffset + renameIndex)); return (shortFilename + fileType); } else { // Fallback behaviour, brute-force/semi-random. bool valid; QString filename; do { valid = true; filename = shortFilename + "-"; for (int i = 0; i < 8; i++) filename.append(QChar(QRandomGenerator::global()->bounded(('Z' - 'A' + 1) + 'A'))); for (int i = 0; i < fileInfos.length(); i++) { int lastSlash = fileInfos[i].GetFilename().lastIndexOf('/'); if (lastSlash < 0) lastSlash = fileInfos[i].GetFilename().lastIndexOf('\\'); if (filename == fileInfos[i].GetFilename().mid(lastSlash + 1)) { valid = false; break; } } } while (!valid); return (filename); } } Heimdall-v2.1.0/heimdall-frontend/source/Packaging.h000066400000000000000000000063201464356164000223620ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef PACKAGING_H #define PACKAGING_H // Qt #include #include #include // Heimdall Frontend #include "PackageData.h" namespace HeimdallFrontend { union TarHeader { enum { kBlockLength = 512, kBlockReadCount = 8, kBlockWriteCount = 8, kTarHeaderLength = 257, kUstarHeaderLength = 500, }; enum { kModeOtherExecute = 1, kModeOtherWrite = 1 << 1, kModeOtherRead = 1 << 2, kModeGroupExecute = 1 << 3, kModeGroupWrite = 1 << 4, kModeGroupRead = 1 << 5, kModeOwnerExecute = 1 << 6, kModeOwnerWrite = 1 << 7, kModeOwnerRead = 1 << 8, kModeReserved = 2 << 9, kModeSetGid = 2 << 10, kModeSetUid = 2 << 11 }; struct { char name[100]; char mode[8]; char userId[8]; char groupId[8]; char size[12]; char modifiedTime[12]; char checksum[8]; char typeFlag; char linkName[100]; char magic[6]; char version[2]; char userName[32]; char groupName[32]; char devMajor[8]; char devMinor[8]; char prefix[155]; } fields; char buffer[kBlockLength]; }; class Packaging { public: // Would definitely prefer to use an enum but VC++ and GCC give conflicting warnings about C++0x or type overflow. static const qint64 kMaxFileSize; private: enum { kExtractBufferLength = 262144, kCompressBufferLength = 262144 }; // TODO: Add support for sparse files to both methods? static bool ExtractTar(QTemporaryFile& tarFile, PackageData *packageData); static bool WriteTarEntry(const QString& filePath, QTemporaryFile *tarFile, const QString& entryFilename); static bool CreateTar(const FirmwareInfo& firmwareInfo, QTemporaryFile *tarFile); // Uses original TAR format. public: static const char *ustarMagic; static bool ExtractPackage(const QString& packagePath, PackageData *packageData); static bool BuildPackage(const QString& packagePath, const FirmwareInfo& firmwareInfo); static QString ClashlessFilename(const QList& fileInfos, int fileInfoIndex); static QString ClashlessFilename(const QList& fileInfos, const QString& filename); }; } #endif Heimdall-v2.1.0/heimdall-frontend/source/aboutform.cpp000066400000000000000000000073571464356164000230420ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Qt #include #include // Heimdall Frontend #include "aboutform.h" #include #define UNUSED(x) (void)(x) using namespace HeimdallFrontend; AboutForm::AboutForm(QWidget *parent) : QWidget(parent) { setupUi(this); // Heimdall Command Line QObject::connect(&heimdallProcess, SIGNAL(readyRead()), this, SLOT(HandleHeimdallStdout())); QObject::connect(&heimdallProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(HandleHeimdallReturned(int, QProcess::ExitStatus))); QObject::connect(&heimdallProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(HandleHeimdallError(QProcess::ProcessError))); heimdallFailed = false; RetrieveHeimdallVersion(); } void AboutForm::RetrieveHeimdallVersion(void) { heimdallProcess.setReadChannel(QProcess::StandardOutput); heimdallProcess.start("heimdall", QStringList("version")); heimdallProcess.waitForFinished(350); // OS X was playing up and not finding heimdall, so we're manually checking the PATH. if (heimdallFailed) { QStringList environment = QProcess::systemEnvironment(); QStringList paths; // Ensure /usr/bin is in PATH for (int i = 0; i < environment.length(); i++) { if (environment[i].left(5) == "PATH=") { paths = environment[i].mid(5).split(':'); paths.prepend("/usr/bin"); break; } } int pathIndex = -1; while (heimdallFailed && ++pathIndex < paths.length()) { QString heimdallPath = paths[pathIndex]; if (heimdallPath.length() > 0) { heimdallFailed = false; if (heimdallPath[heimdallPath.length() - 1] != QDir::separator()) heimdallPath += QDir::separator(); heimdallPath += "heimdall"; heimdallProcess.start(heimdallPath, QStringList("version")); heimdallProcess.waitForFinished(350); } } if (heimdallFailed) versionCopyrightLabel->setText(versionCopyrightLabel->text().replace("%HEIMDALL-VERSION%", "")); } } void AboutForm::HandleHeimdallStdout(void) { QString version = heimdallProcess.readAll(); if (version.length() > 0) { if (version.at(0) == QChar('v')) version = version.mid(1); versionCopyrightLabel->setText(versionCopyrightLabel->text().replace("%HEIMDALL-VERSION%", "Version " + version + "
")); } } void AboutForm::HandleHeimdallReturned(int exitCode, QProcess::ExitStatus exitStatus) { UNUSED(exitCode); UNUSED(exitStatus); // If for some reason %HEIMDALL-VERSION% hasn't been replaced yet, we'll replace it with an empty string. versionCopyrightLabel->setText(versionCopyrightLabel->text().replace("%HEIMDALL-VERSION%", "")); } void AboutForm::HandleHeimdallError(QProcess::ProcessError error) { UNUSED(error); heimdallFailed = true; } Heimdall-v2.1.0/heimdall-frontend/source/aboutform.h000066400000000000000000000033061464356164000224750ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef ABOUTFORM_H #define ABOUTFORM_H // Qt #include #include // Heimdall Frontend #include "ui_aboutform.h" namespace HeimdallFrontend { class AboutForm : public QWidget, public Ui::AboutForm { Q_OBJECT private: bool heimdallFailed; QProcess heimdallProcess; void RetrieveHeimdallVersion(void); public: explicit AboutForm(QWidget *parent = 0); public slots: // Heimdall Command Line void HandleHeimdallStdout(void); void HandleHeimdallReturned(int exitCode, QProcess::ExitStatus exitStatus); void HandleHeimdallError(QProcess::ProcessError error); }; } #endif Heimdall-v2.1.0/heimdall-frontend/source/main.cpp000066400000000000000000000026571464356164000217660ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Qt #include #include // Heimdall Frontend #include "mainwindow.h" #if defined(QT_STATIC) Q_IMPORT_PLUGIN (QWindowsIntegrationPlugin); #endif using namespace HeimdallFrontend; int main(int argc, char *argv[]) { QApplication application(argc, argv); MainWindow window; window.show(); return (application.exec()); } Heimdall-v2.1.0/heimdall-frontend/source/mainwindow.cpp000066400000000000000000001173771464356164000232240ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Qt #include #include #include #include #include #include #include // Heimdall Frontend #include "Alerts.h" #include "mainwindow.h" #include "Packaging.h" #define UNUSED(x) (void)(x) using namespace HeimdallFrontend; void MainWindow::StartHeimdall(const QStringList& arguments) { UpdateInterfaceAvailability(); heimdallProcess.setReadChannel(QProcess::StandardOutput); heimdallProcess.start("heimdall", arguments); heimdallProcess.waitForStarted(3000); // OS X was playing up and not finding heimdall, so we're manually checking the PATH. if (heimdallFailed) { QStringList environment = QProcess::systemEnvironment(); QStringList paths; // Ensure /usr/local/bin and /usr/bin are in PATH. for (int i = 0; i < environment.length(); i++) { if (environment[i].left(5) == "PATH=") { paths = environment[i].mid(5).split(':'); if (!paths.contains("/usr/local/bin")) paths.prepend("/usr/local/bin"); if (!paths.contains("/usr/bin")) paths.prepend("/usr/bin"); break; } } int pathIndex = -1; while (heimdallFailed && ++pathIndex < paths.length()) { QString heimdallPath = paths[pathIndex]; if (heimdallPath.length() > 0) { utilityOutputPlainTextEdit->clear(); heimdallFailed = false; if (heimdallPath[heimdallPath.length() - 1] != QDir::separator()) heimdallPath += QDir::separator(); heimdallPath += "heimdall"; heimdallProcess.start(heimdallPath, arguments); heimdallProcess.waitForStarted(3000); } } if (heimdallFailed) { flashLabel->setText("Failed to start Heimdall!"); heimdallState = HeimdallState::Stopped; UpdateInterfaceAvailability(); } } } void MainWindow::UpdateUnusedPartitionIds(void) { unusedPartitionIds.clear(); // Initially populate unusedPartitionIds with all possible partition IDs. for (unsigned int i = 0; i < currentPitData.GetEntryCount(); i++) { const PitEntry *pitEntry = currentPitData.GetEntry(i); if (pitEntry->IsFlashable() && strcmp(pitEntry->GetPartitionName(), "PIT") != 0 && strcmp(pitEntry->GetPartitionName(), "PT") != 0) unusedPartitionIds.append(pitEntry->GetIdentifier()); } // Remove any used partition IDs from unusedPartitionIds QList& fileList = workingPackageData.GetFirmwareInfo().GetFileInfos(); for (int i = 0; i < fileList.length(); i++) unusedPartitionIds.removeOne(fileList[i].GetPartitionId()); } bool MainWindow::ReadPit(QFile *file) { if(!file->open(QIODevice::ReadOnly)) return (false); unsigned char *buffer = new unsigned char[file->size()]; file->read(reinterpret_cast(buffer), file->size()); file->close(); bool success = currentPitData.Unpack(buffer); delete[] buffer; if (!success) currentPitData.Clear(); return (success); } void MainWindow::UpdatePackageUserInterface(void) { supportedDevicesListWidget->clear(); includedFilesListWidget->clear(); if (loadedPackageData.IsCleared()) { // Package Interface firmwareNameLineEdit->clear(); versionLineEdit->clear(); developerNamesLineEdit->clear(); platformLineEdit->clear(); repartitionRadioButton->setChecked(false); noRebootRadioButton->setChecked(false); } else { firmwareNameLineEdit->setText(loadedPackageData.GetFirmwareInfo().GetName()); versionLineEdit->setText(loadedPackageData.GetFirmwareInfo().GetVersion()); QString developerNames; if (!loadedPackageData.GetFirmwareInfo().GetDevelopers().isEmpty()) { developerNames = loadedPackageData.GetFirmwareInfo().GetDevelopers()[0]; for (int i = 1; i < loadedPackageData.GetFirmwareInfo().GetDevelopers().length(); i++) developerNames += ", " + loadedPackageData.GetFirmwareInfo().GetDevelopers()[i]; } developerNamesLineEdit->setText(developerNames); platformLineEdit->setText(loadedPackageData.GetFirmwareInfo().GetPlatformInfo().GetName() + " (" + loadedPackageData.GetFirmwareInfo().GetPlatformInfo().GetVersion() + ")"); for (int i = 0; i < loadedPackageData.GetFirmwareInfo().GetDeviceInfos().length(); i++) { const DeviceInfo& deviceInfo = loadedPackageData.GetFirmwareInfo().GetDeviceInfos()[i]; supportedDevicesListWidget->addItem(deviceInfo.GetManufacturer() + " " + deviceInfo.GetName() + ": " + deviceInfo.GetProduct()); } for (int i = 0; i < loadedPackageData.GetFirmwareInfo().GetFileInfos().length(); i++) { const FileInfo& fileInfo = loadedPackageData.GetFirmwareInfo().GetFileInfos()[i]; includedFilesListWidget->addItem(fileInfo.GetFilename()); } repartitionRadioButton->setChecked(loadedPackageData.GetFirmwareInfo().GetRepartition()); noRebootRadioButton->setChecked(loadedPackageData.GetFirmwareInfo().GetNoReboot()); } UpdateLoadPackageInterfaceAvailability(); } bool MainWindow::IsArchive(QString path) { // Not a real check but hopefully it gets the message across, don't directly flash archives! return (path.endsWith(".tar", Qt::CaseInsensitive) || path.endsWith(".gz", Qt::CaseInsensitive) || path.endsWith(".zip", Qt::CaseInsensitive) || path.endsWith(".bz2", Qt::CaseInsensitive) || path.endsWith(".7z", Qt::CaseInsensitive) || path.endsWith(".rar", Qt::CaseInsensitive)); } QString MainWindow::PromptFileSelection(const QString& caption, const QString& filter) { QString path = QFileDialog::getOpenFileName(this, caption, lastDirectory, filter); if (path != "") lastDirectory = path.left(path.lastIndexOf('/') + 1); return (path); } QString MainWindow::PromptFileCreation(const QString& caption, const QString& filter) { QString path = QFileDialog::getSaveFileName(this, caption, lastDirectory, filter); if (path != "") lastDirectory = path.left(path.lastIndexOf('/') + 1); return (path); } void MainWindow::UpdateLoadPackageInterfaceAvailability(void) { if (loadedPackageData.IsCleared()) { developerHomepageButton->setEnabled(false); developerDonateButton->setEnabled(false); loadFirmwareButton->setEnabled(false); } else { developerHomepageButton->setEnabled(!loadedPackageData.GetFirmwareInfo().GetUrl().isEmpty()); developerDonateButton->setEnabled(!loadedPackageData.GetFirmwareInfo().GetDonateUrl().isEmpty()); loadFirmwareButton->setEnabled(!!(heimdallState & HeimdallState::Stopped)); } } void MainWindow::UpdateFlashInterfaceAvailability(void) { if (!!(heimdallState & HeimdallState::Stopped)) { partitionNameComboBox->setEnabled(partitionsListWidget->currentRow() >= 0); bool allPartitionsValid = true; QList& fileList = workingPackageData.GetFirmwareInfo().GetFileInfos(); for (int i = 0; i < fileList.length(); i++) { if (fileList[i].GetFilename().isEmpty()) { allPartitionsValid = false; break; } } bool validFlashSettings = allPartitionsValid && fileList.length() > 0; flashProgressBar->setEnabled(false); optionsGroup->setEnabled(true); sessionGroup->setEnabled(true); startFlashButton->setEnabled(validFlashSettings); noRebootCheckBox->setEnabled(validFlashSettings); resumeCheckbox->setEnabled(validFlashSettings); waitForDeviceCheckbox->setEnabled(validFlashSettings); } else { partitionNameComboBox->setEnabled(false); flashProgressBar->setEnabled(true); optionsGroup->setEnabled(false); sessionGroup->setEnabled(false); } } void MainWindow::UpdateCreatePackageInterfaceAvailability(void) { if (!!(heimdallState & HeimdallState::Stopped)) { const FirmwareInfo& firmwareInfo = workingPackageData.GetFirmwareInfo(); bool fieldsPopulated = !(firmwareInfo.GetName().isEmpty() || firmwareInfo.GetVersion().isEmpty() || firmwareInfo.GetPlatformInfo().GetName().isEmpty() || firmwareInfo.GetPlatformInfo().GetVersion().isEmpty() || firmwareInfo.GetDevelopers().isEmpty() || firmwareInfo.GetDeviceInfos().isEmpty()); buildPackageButton->setEnabled(fieldsPopulated); addDeveloperButton->setEnabled(!addDeveloperButton->text().isEmpty()); removeDeveloperButton->setEnabled(createDevelopersListWidget->currentRow() >= 0); } else { buildPackageButton->setEnabled(false); } } void MainWindow::UpdateUtilitiesInterfaceAvailability(void) { if (!!(heimdallState & HeimdallState::Stopped)) { detectDeviceButton->setEnabled(true); closePcScreenButton->setEnabled(true); pitSaveAsButton->setEnabled(true); downloadPitButton->setEnabled(!pitDestinationLineEdit->text().isEmpty()); if (printPitDeviceRadioBox->isChecked()) { // Device printLocalPitGroup->setEnabled(false); printPitButton->setEnabled(true); } else { // Local File printLocalPitGroup->setEnabled(true); printLocalPitLineEdit->setEnabled(true); printLocalPitBrowseButton->setEnabled(true); printPitButton->setEnabled(!printLocalPitLineEdit->text().isEmpty()); } } else { detectDeviceButton->setEnabled(false); closePcScreenButton->setEnabled(false); pitSaveAsButton->setEnabled(false); downloadPitButton->setEnabled(false); printLocalPitGroup->setEnabled(false); printPitButton->setEnabled(false); } } void MainWindow::UpdateInterfaceAvailability(void) { UpdateLoadPackageInterfaceAvailability(); UpdateFlashInterfaceAvailability(); UpdateCreatePackageInterfaceAvailability(); UpdateUtilitiesInterfaceAvailability(); if (!!(heimdallState & HeimdallState::Stopped)) { // Enable/disable tabs for (int i = 0; i < functionTabWidget->count(); i++) functionTabWidget->setTabEnabled(i, true); functionTabWidget->setTabEnabled(functionTabWidget->indexOf(createPackageTab), startFlashButton->isEnabled()); } else { // Disable non-current tabs for (int i = 0; i < functionTabWidget->count(); i++) { if (i == functionTabWidget->currentIndex()) functionTabWidget->setTabEnabled(i, true); else functionTabWidget->setTabEnabled(i, false); } } } void MainWindow::UpdatePartitionNamesInterface(void) { populatingPartitionNames = true; partitionNameComboBox->clear(); int partitionsListWidgetRow = partitionsListWidget->currentRow(); if (partitionsListWidgetRow >= 0) { const FileInfo& partitionInfo = workingPackageData.GetFirmwareInfo().GetFileInfos()[partitionsListWidget->currentRow()]; for (int i = 0; i < unusedPartitionIds.length(); i++) partitionNameComboBox->addItem(currentPitData.FindEntry(unusedPartitionIds[i])->GetPartitionName()); partitionNameComboBox->addItem(currentPitData.FindEntry(partitionInfo.GetPartitionId())->GetPartitionName()); partitionNameComboBox->setCurrentIndex(unusedPartitionIds.length()); } populatingPartitionNames = false; UpdateFlashInterfaceAvailability(); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setupUi(this); heimdallState = HeimdallState::Stopped; lastDirectory = QDir::toNativeSeparators(QApplication::applicationDirPath()); populatingPartitionNames = false; verboseOutput = false; resume = false; waitForDevice = false; tabIndex = functionTabWidget->currentIndex(); functionTabWidget->setTabEnabled(functionTabWidget->indexOf(createPackageTab), false); QObject::connect(functionTabWidget, SIGNAL(currentChanged(int)), this, SLOT(FunctionTabChanged(int))); // Menu QObject::connect(actionVerboseOutput, SIGNAL(toggled(bool)), this, SLOT(SetVerboseOutput(bool))); QObject::connect(actionResumeConnection, SIGNAL(toggled(bool)), this, SLOT(SetResume(bool))); QObject::connect(actionAboutHeimdall, SIGNAL(triggered()), this, SLOT(ShowAbout())); // Load Package Tab QObject::connect(browseFirmwarePackageButton, SIGNAL(clicked()), this, SLOT(SelectFirmwarePackage())); QObject::connect(developerHomepageButton, SIGNAL(clicked()), this, SLOT(OpenDeveloperHomepage())); QObject::connect(developerDonateButton, SIGNAL(clicked()), this, SLOT(OpenDeveloperDonationWebpage())); QObject::connect(loadFirmwareButton, SIGNAL(clicked()), this, SLOT(LoadFirmwarePackage())); QObject::connect(partitionsListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(SelectPartition(int))); QObject::connect(addPartitionButton, SIGNAL(clicked()), this, SLOT(AddPartition())); QObject::connect(removePartitionButton, SIGNAL(clicked()), this, SLOT(RemovePartition())); // Flash Tab QObject::connect(partitionNameComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SelectPartitionName(int))); QObject::connect(partitionFileBrowseButton, SIGNAL(clicked()), this, SLOT(SelectPartitionFile())); QObject::connect(pitBrowseButton, SIGNAL(clicked()), this, SLOT(SelectPit())); QObject::connect(repartitionCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetRepartition(int))); QObject::connect(noRebootCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetNoReboot(int))); QObject::connect(resumeCheckbox, SIGNAL(stateChanged(int)), this, SLOT(SetResume(int))); QObject::connect(waitForDeviceCheckbox, SIGNAL(stateChanged(int)), this, SLOT(SetWaitForDevice(int))); QObject::connect(startFlashButton, SIGNAL(clicked()), this, SLOT(StartFlash())); // Create Package Tab QObject::connect(createFirmwareNameLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(FirmwareNameChanged(const QString&))); QObject::connect(createFirmwareVersionLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(FirmwareVersionChanged(const QString&))); QObject::connect(createPlatformNameLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(PlatformNameChanged(const QString&))); QObject::connect(createPlatformVersionLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(PlatformVersionChanged(const QString&))); QObject::connect(createHomepageLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(HomepageUrlChanged(const QString&))); QObject::connect(createDonateLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(DonateUrlChanged(const QString&))); QObject::connect(createDevelopersListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(SelectDeveloper(int))); QObject::connect(createDeveloperNameLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(DeveloperNameChanged(const QString&))); QObject::connect(addDeveloperButton, SIGNAL(clicked()), this, SLOT(AddDeveloper())); QObject::connect(removeDeveloperButton, SIGNAL(clicked()), this, SLOT(RemoveDeveloper())); QObject::connect(createDevicesListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(SelectDevice(int))); QObject::connect(deviceManufacturerLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(DeviceInfoChanged(const QString&))); QObject::connect(deviceNameLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(DeviceInfoChanged(const QString&))); QObject::connect(deviceProductCodeLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(DeviceInfoChanged(const QString&))); QObject::connect(addDeviceButton, SIGNAL(clicked()), this, SLOT(AddDevice())); QObject::connect(removeDeviceButton, SIGNAL(clicked()), this, SLOT(RemoveDevice())); QObject::connect(buildPackageButton, SIGNAL(clicked()), this, SLOT(BuildPackage())); // Utilities Tab QObject::connect(detectDeviceButton, SIGNAL(clicked()), this, SLOT(DetectDevice())); QObject::connect(closePcScreenButton, SIGNAL(clicked()), this, SLOT(ClosePcScreen())); QObject::connect(printPitDeviceRadioBox, SIGNAL(toggled(bool)), this, SLOT(DevicePrintPitToggled(bool))); QObject::connect(printPitLocalFileRadioBox, SIGNAL(toggled(bool)), this, SLOT(LocalFilePrintPitToggled(bool))); QObject::connect(printLocalPitBrowseButton, SIGNAL(clicked()), this, SLOT(SelectPrintPitFile())); QObject::connect(printPitButton, SIGNAL(clicked()), this, SLOT(PrintPit())); QObject::connect(pitSaveAsButton, SIGNAL(clicked()), this, SLOT(SelectPitDestination())); QObject::connect(downloadPitButton, SIGNAL(clicked()), this, SLOT(DownloadPit())); // Heimdall Command Line QObject::connect(&heimdallProcess, SIGNAL(readyRead()), this, SLOT(HandleHeimdallStdout())); QObject::connect(&heimdallProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(HandleHeimdallReturned(int, QProcess::ExitStatus))); QObject::connect(&heimdallProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(HandleHeimdallError(QProcess::ProcessError))); } MainWindow::~MainWindow() { } void MainWindow::OpenDonationWebpage(void) { QDesktopServices::openUrl(QUrl("https://www.glassechidna.com.au/donate/", QUrl::StrictMode)); } void MainWindow::SetVerboseOutput(bool enabled) { verboseOutput = enabled; } void MainWindow::ShowAbout(void) { aboutForm.show(); } void MainWindow::FunctionTabChanged(int index) { tabIndex = index; deviceDetectedRadioButton->setChecked(false); } void MainWindow::SelectFirmwarePackage(void) { loadedPackageData.Clear(); UpdatePackageUserInterface(); QString path = PromptFileSelection("Select Package", "Firmware Package (*.gz)"); firmwarePackageLineEdit->setText(path); if (firmwarePackageLineEdit->text() != "") { if (Packaging::ExtractPackage(firmwarePackageLineEdit->text(), &loadedPackageData)) UpdatePackageUserInterface(); else loadedPackageData.Clear(); } } void MainWindow::OpenDeveloperHomepage(void) { if(!QDesktopServices::openUrl(QUrl(loadedPackageData.GetFirmwareInfo().GetUrl(), QUrl::TolerantMode))) Alerts::DisplayWarning(QString("Cannot open invalid URL:\n%1").arg(loadedPackageData.GetFirmwareInfo().GetUrl())); } void MainWindow::OpenDeveloperDonationWebpage(void) { if (!QDesktopServices::openUrl(QUrl(loadedPackageData.GetFirmwareInfo().GetDonateUrl(), QUrl::TolerantMode))) Alerts::DisplayWarning(QString("Cannot open invalid URL:\n%1").arg(loadedPackageData.GetFirmwareInfo().GetDonateUrl())); } void MainWindow::LoadFirmwarePackage(void) { workingPackageData.Clear(); currentPitData.Clear(); workingPackageData.GetFiles().append(loadedPackageData.GetFiles()); loadedPackageData.RemoveAllFiles(); const QList packageFileInfos = loadedPackageData.GetFirmwareInfo().GetFileInfos(); for (int i = 0; i < packageFileInfos.length(); i++) { bool fileFound = false; for (int j = 0; j < workingPackageData.GetFiles().length(); j++) { if (workingPackageData.GetFiles()[j]->fileTemplate() == ("XXXXXX-" + packageFileInfos[i].GetFilename())) { FileInfo partitionInfo(packageFileInfos[i].GetPartitionId(), QDir::current().absoluteFilePath(workingPackageData.GetFiles()[j]->fileName())); workingPackageData.GetFirmwareInfo().GetFileInfos().append(partitionInfo); fileFound = true; break; } } if (!fileFound) Alerts::DisplayWarning(QString("%1 is missing from the package.").arg(packageFileInfos[i].GetFilename())); } // Find the PIT file and read it for (int i = 0; i < workingPackageData.GetFiles().length(); i++) { QTemporaryFile *file = workingPackageData.GetFiles()[i]; if (file->fileTemplate() == ("XXXXXX-" + loadedPackageData.GetFirmwareInfo().GetPitFilename())) { workingPackageData.GetFirmwareInfo().SetPitFilename(QDir::current().absoluteFilePath(file->fileName())); if (!ReadPit(file)) { Alerts::DisplayError("Failed to read PIT file."); loadedPackageData.Clear(); UpdatePackageUserInterface(); workingPackageData.Clear(); UpdateUnusedPartitionIds(); return; } break; } } UpdateUnusedPartitionIds(); workingPackageData.GetFirmwareInfo().SetRepartition(loadedPackageData.GetFirmwareInfo().GetRepartition()); workingPackageData.GetFirmwareInfo().SetNoReboot(loadedPackageData.GetFirmwareInfo().GetNoReboot()); loadedPackageData.Clear(); UpdatePackageUserInterface(); firmwarePackageLineEdit->clear(); partitionsListWidget->clear(); // Populate partitionsListWidget with partition names (from the PIT file) for (int i = 0; i < workingPackageData.GetFirmwareInfo().GetFileInfos().length(); i++) { const FileInfo& partitionInfo = workingPackageData.GetFirmwareInfo().GetFileInfos()[i]; const PitEntry *pitEntry = currentPitData.FindEntry(partitionInfo.GetPartitionId()); if (pitEntry) { partitionsListWidget->addItem(pitEntry->GetPartitionName()); } else { Alerts::DisplayError("Firmware package includes invalid partition IDs."); loadedPackageData.GetFirmwareInfo().Clear(); currentPitData.Clear(); UpdateUnusedPartitionIds(); partitionsListWidget->clear(); return; } } partitionNameComboBox->clear(); partitionIdLineEdit->clear(); partitionFileLineEdit->clear(); partitionFileBrowseButton->setEnabled(false); repartitionCheckBox->setEnabled(true); repartitionCheckBox->setChecked(workingPackageData.GetFirmwareInfo().GetRepartition()); noRebootCheckBox->setEnabled(true); noRebootCheckBox->setChecked(workingPackageData.GetFirmwareInfo().GetNoReboot()); partitionsListWidget->setEnabled(true); addPartitionButton->setEnabled(true); removePartitionButton->setEnabled(partitionsListWidget->currentRow() >= 0); pitLineEdit->setText(workingPackageData.GetFirmwareInfo().GetPitFilename()); functionTabWidget->setCurrentWidget(flashTab); UpdateInterfaceAvailability(); } void MainWindow::SelectPartitionName(int index) { if (!populatingPartitionNames && index != -1 && index != unusedPartitionIds.length()) { unsigned int newPartitionIndex = unusedPartitionIds[index]; unusedPartitionIds.removeAt(index); FileInfo& fileInfo = workingPackageData.GetFirmwareInfo().GetFileInfos()[partitionsListWidget->currentRow()]; unusedPartitionIds.append(fileInfo.GetPartitionId()); fileInfo.SetPartitionId(newPartitionIndex); PitEntry *pitEntry = currentPitData.FindEntry(newPartitionIndex); QString title("File"); if (pitEntry && strlen(pitEntry->GetFlashFilename()) > 0) title += " (" + QString(pitEntry->GetFlashFilename()) + ")"; partitionFileGroup->setTitle(title); if (pitEntry && !fileInfo.GetFilename().isEmpty()) { QString partitionFilename = pitEntry->GetFlashFilename(); int lastPeriod = partitionFilename.lastIndexOf(QChar('.')); if (lastPeriod >= 0) { QString partitionFileExtension = partitionFilename.mid(lastPeriod + 1); lastPeriod = fileInfo.GetFilename().lastIndexOf(QChar('.')); if (lastPeriod < 0 || fileInfo.GetFilename().mid(lastPeriod + 1) != partitionFileExtension) Alerts::DisplayWarning(QString("%1 partition expects files with file extension \"%2\".").arg(pitEntry->GetPartitionName(), partitionFileExtension)); } } partitionNameComboBox->clear(); // Update interface UpdatePartitionNamesInterface(); partitionIdLineEdit->setText(QString::number(newPartitionIndex)); partitionsListWidget->currentItem()->setText(currentPitData.FindEntry(newPartitionIndex)->GetPartitionName()); } } void MainWindow::SelectPartitionFile(void) { QString path = PromptFileSelection(); if (path != "") { FileInfo& fileInfo = workingPackageData.GetFirmwareInfo().GetFileInfos()[partitionsListWidget->currentRow()]; PitEntry *pitEntry = currentPitData.FindEntry(fileInfo.GetPartitionId()); QString partitionFilename = pitEntry->GetFlashFilename(); int lastPeriod = partitionFilename.lastIndexOf(QChar('.')); if (lastPeriod >= 0) { QString partitionFileExtension = partitionFilename.mid(lastPeriod + 1); lastPeriod = path.lastIndexOf(QChar('.')); if (lastPeriod < 0 || path.mid(lastPeriod + 1) != partitionFileExtension) Alerts::DisplayWarning(QString("%1 partition expects files with file extension \"%2\".").arg(pitEntry->GetPartitionName(), partitionFileExtension)); } fileInfo.SetFilename(path); partitionFileLineEdit->setText(path); pitBrowseButton->setEnabled(true); partitionsListWidget->setEnabled(true); UpdateInterfaceAvailability(); if (unusedPartitionIds.length() > 0) addPartitionButton->setEnabled(true); } } void MainWindow::SelectPartition(int row) { if (row >= 0) { const FileInfo& partitionInfo = workingPackageData.GetFirmwareInfo().GetFileInfos()[row]; UpdatePartitionNamesInterface(); partitionIdLineEdit->setText(QString::number(partitionInfo.GetPartitionId())); partitionFileLineEdit->setText(partitionInfo.GetFilename()); partitionFileBrowseButton->setEnabled(true); removePartitionButton->setEnabled(true); QString title("File"); PitEntry *pitEntry = currentPitData.FindEntry(partitionInfo.GetPartitionId()); if (pitEntry && strlen(pitEntry->GetFlashFilename()) > 0) title += " (" + QString(pitEntry->GetFlashFilename()) + ")"; partitionFileGroup->setTitle(title); } else { UpdatePartitionNamesInterface(); partitionIdLineEdit->clear(); partitionFileLineEdit->clear(); partitionFileBrowseButton->setEnabled(false); removePartitionButton->setEnabled(false); partitionFileGroup->setTitle("File"); } } void MainWindow::AddPartition(void) { FileInfo partitionInfo(unusedPartitionIds.first(), ""); workingPackageData.GetFirmwareInfo().GetFileInfos().append(partitionInfo); UpdateUnusedPartitionIds(); pitBrowseButton->setEnabled(false); addPartitionButton->setEnabled(false); partitionsListWidget->addItem(currentPitData.FindEntry(partitionInfo.GetPartitionId())->GetPartitionName()); partitionsListWidget->setCurrentRow(partitionsListWidget->count() - 1); partitionsListWidget->setEnabled(false); UpdateInterfaceAvailability(); } void MainWindow::RemovePartition(void) { workingPackageData.GetFirmwareInfo().GetFileInfos().removeAt(partitionsListWidget->currentRow()); UpdateUnusedPartitionIds(); QListWidgetItem *item = partitionsListWidget->currentItem(); partitionsListWidget->setCurrentRow(-1); delete item; pitBrowseButton->setEnabled(true); addPartitionButton->setEnabled(true); partitionsListWidget->setEnabled(true); UpdateInterfaceAvailability(); } void MainWindow::SelectPit(void) { QString path = PromptFileSelection("Select PIT", "*.pit"); bool validPit = path != ""; if (validPit) { // In order to map files in the old PIT to file in the new one, we first must use partition names instead of IDs. QList fileInfos = workingPackageData.GetFirmwareInfo().GetFileInfos(); int partitionNamesCount = fileInfos.length(); QString *partitionNames = new QString[fileInfos.length()]; for (int i = 0; i < fileInfos.length(); i++) partitionNames[i] = currentPitData.FindEntry(fileInfos[i].GetPartitionId())->GetPartitionName(); currentPitData.Clear(); QFile pitFile(path); if (ReadPit(&pitFile)) { workingPackageData.GetFirmwareInfo().SetPitFilename(path); partitionsListWidget->clear(); int partitionInfoIndex = 0; for (int i = 0; i < partitionNamesCount; i++) { const PitEntry *pitEntry = currentPitData.FindEntry(partitionNames[i].toLatin1().constData()); if (pitEntry) { fileInfos[partitionInfoIndex++].SetPartitionId(pitEntry->GetIdentifier()); partitionsListWidget->addItem(pitEntry->GetPartitionName()); } else { fileInfos.removeAt(partitionInfoIndex); } } } else { validPit = false; } // If the selected PIT was invalid, attempt to reload the old one. if (!validPit) { Alerts::DisplayError("The file selected was not a valid PIT file."); if (!workingPackageData.GetFirmwareInfo().GetPitFilename().isEmpty()) { QFile originalPitFile(workingPackageData.GetFirmwareInfo().GetPitFilename()); if (ReadPit(&originalPitFile)) { validPit = true; } else { Alerts::DisplayError("Failed to reload working PIT data."); workingPackageData.Clear(); partitionsListWidget->clear(); } } } UpdateUnusedPartitionIds(); delete [] partitionNames; pitLineEdit->setText(workingPackageData.GetFirmwareInfo().GetPitFilename()); repartitionCheckBox->setEnabled(validPit); noRebootCheckBox->setEnabled(validPit); partitionsListWidget->setEnabled(validPit); addPartitionButton->setEnabled(validPit); removePartitionButton->setEnabled(validPit && partitionsListWidget->currentRow() >= 0); UpdateInterfaceAvailability(); } } void MainWindow::SetRepartition(int enabled) { workingPackageData.GetFirmwareInfo().SetRepartition(enabled); repartitionCheckBox->setChecked(enabled); } void MainWindow::SetNoReboot(int enabled) { workingPackageData.GetFirmwareInfo().SetNoReboot(enabled); noRebootCheckBox->setChecked(enabled); } void MainWindow::SetResume(bool enabled) { resume = enabled; actionResumeConnection->setChecked(enabled); resumeCheckbox->setChecked(enabled); } void MainWindow::SetResume(int enabled) { SetResume(enabled != 0); } void MainWindow::SetWaitForDevice(bool enabled) { waitForDevice = enabled; waitForDeviceCheckbox->setChecked(enabled); } void MainWindow::SetWaitForDevice(int enabled) { SetWaitForDevice(enabled != 0); } void MainWindow::StartFlash(void) { outputPlainTextEdit->clear(); heimdallState = HeimdallState::Flashing; heimdallFailed = false; const FirmwareInfo& firmwareInfo = workingPackageData.GetFirmwareInfo(); const QList& fileInfos = firmwareInfo.GetFileInfos(); QStringList arguments; arguments.append("flash"); if (firmwareInfo.GetRepartition()) arguments.append("--repartition"); arguments.append("--pit"); arguments.append(firmwareInfo.GetPitFilename()); for (int i = 0; i < fileInfos.length(); i++) { QString flag; flag = QString::asprintf("--%u", fileInfos[i].GetPartitionId()); arguments.append(flag); arguments.append(fileInfos[i].GetFilename()); } if (firmwareInfo.GetNoReboot()) { arguments.append("--no-reboot"); heimdallState |= HeimdallState::NoReboot; } if (resume) arguments.append("--resume"); if (waitForDevice) arguments.append("--wait"); if (verboseOutput) arguments.append("--verbose"); arguments.append("--stdout-errors"); StartHeimdall(arguments); } void MainWindow::FirmwareNameChanged(const QString& text) { workingPackageData.GetFirmwareInfo().SetName(text); UpdateInterfaceAvailability(); } void MainWindow::FirmwareVersionChanged(const QString& text) { workingPackageData.GetFirmwareInfo().SetVersion(text); UpdateInterfaceAvailability(); } void MainWindow::PlatformNameChanged(const QString& text) { workingPackageData.GetFirmwareInfo().GetPlatformInfo().SetName(text); UpdateInterfaceAvailability(); } void MainWindow::PlatformVersionChanged(const QString& text) { workingPackageData.GetFirmwareInfo().GetPlatformInfo().SetVersion(text); UpdateInterfaceAvailability(); } void MainWindow::HomepageUrlChanged(const QString& text) { workingPackageData.GetFirmwareInfo().SetUrl(text); } void MainWindow::DonateUrlChanged(const QString& text) { workingPackageData.GetFirmwareInfo().SetDonateUrl(text); } void MainWindow::DeveloperNameChanged(const QString& text) { UNUSED(text); UpdateCreatePackageInterfaceAvailability(); } void MainWindow::SelectDeveloper(int row) { UNUSED(row); UpdateCreatePackageInterfaceAvailability(); } void MainWindow::AddDeveloper(void) { workingPackageData.GetFirmwareInfo().GetDevelopers().append(createDeveloperNameLineEdit->text()); createDevelopersListWidget->addItem(createDeveloperNameLineEdit->text()); createDeveloperNameLineEdit->clear(); UpdateCreatePackageInterfaceAvailability(); } void MainWindow::RemoveDeveloper(void) { workingPackageData.GetFirmwareInfo().GetDevelopers().removeAt(createDevelopersListWidget->currentRow()); QListWidgetItem *item = createDevelopersListWidget->currentItem(); createDevelopersListWidget->setCurrentRow(-1); delete item; removeDeveloperButton->setEnabled(false); UpdateInterfaceAvailability(); } void MainWindow::DeviceInfoChanged(const QString& text) { UNUSED(text); if (deviceManufacturerLineEdit->text().isEmpty() || deviceNameLineEdit->text().isEmpty() || deviceProductCodeLineEdit->text().isEmpty()) addDeviceButton->setEnabled(false); else addDeviceButton->setEnabled(true); } void MainWindow::SelectDevice(int row) { removeDeviceButton->setEnabled(row >= 0); } void MainWindow::AddDevice(void) { workingPackageData.GetFirmwareInfo().GetDeviceInfos().append(DeviceInfo(deviceManufacturerLineEdit->text(), deviceProductCodeLineEdit->text(), deviceNameLineEdit->text())); createDevicesListWidget->addItem(deviceManufacturerLineEdit->text() + " " + deviceNameLineEdit->text() + ": " + deviceProductCodeLineEdit->text()); deviceManufacturerLineEdit->clear(); deviceNameLineEdit->clear(); deviceProductCodeLineEdit->clear(); UpdateInterfaceAvailability(); } void MainWindow::RemoveDevice(void) { workingPackageData.GetFirmwareInfo().GetDeviceInfos().removeAt(createDevicesListWidget->currentRow()); QListWidgetItem *item = createDevicesListWidget->currentItem(); createDevicesListWidget->setCurrentRow(-1); delete item; removeDeviceButton->setEnabled(false); UpdateInterfaceAvailability(); } void MainWindow::BuildPackage(void) { QString packagePath = PromptFileCreation("Save Package", "Firmware Package (*.gz)"); if (!packagePath.isEmpty()) { if (!packagePath.endsWith(".tar.gz", Qt::CaseInsensitive)) { if (packagePath.endsWith(".tar", Qt::CaseInsensitive)) packagePath.append(".gz"); else if (packagePath.endsWith(".gz", Qt::CaseInsensitive)) packagePath.replace(packagePath.length() - 3, ".tar.gz"); else if (packagePath.endsWith(".tgz", Qt::CaseInsensitive)) packagePath.replace(packagePath.length() - 4, ".tar.gz"); else packagePath.append(".tar.gz"); } Packaging::BuildPackage(packagePath, workingPackageData.GetFirmwareInfo()); } } void MainWindow::DetectDevice(void) { deviceDetectedRadioButton->setChecked(false); utilityOutputPlainTextEdit->clear(); heimdallState = HeimdallState::DetectingDevice; heimdallFailed = false; QStringList arguments; arguments.append("detect"); if (waitForDevice) arguments.append("--wait"); if (verboseOutput) arguments.append("--verbose"); arguments.append("--stdout-errors"); StartHeimdall(arguments); } void MainWindow::ClosePcScreen(void) { utilityOutputPlainTextEdit->clear(); heimdallState = HeimdallState::ClosingPcScreen; heimdallFailed = false; QStringList arguments; arguments.append("close-pc-screen"); if (resume) arguments.append("--resume"); if (waitForDevice) arguments.append("--wait"); if (verboseOutput) arguments.append("--verbose"); arguments.append("--stdout-errors"); StartHeimdall(arguments); } void MainWindow::SelectPitDestination(void) { QString path = PromptFileCreation("Save PIT", "*.pit"); if (path != "") { if (!path.endsWith(".pit")) path.append(".pit"); pitDestinationLineEdit->setText(path); UpdateInterfaceAvailability(); } } void MainWindow::DownloadPit(void) { deviceDetectedRadioButton->setChecked(false); utilityOutputPlainTextEdit->clear(); heimdallState = HeimdallState::DownloadingPit | HeimdallState::NoReboot; heimdallFailed = false; QStringList arguments; arguments.append("download-pit"); arguments.append("--output"); arguments.append(pitDestinationLineEdit->text()); arguments.append("--no-reboot"); if (resume) arguments.append("--resume"); if (waitForDevice) arguments.append("--wait"); if (verboseOutput) arguments.append("--verbose"); arguments.append("--stdout-errors"); StartHeimdall(arguments); } void MainWindow::DevicePrintPitToggled(bool checked) { if (checked) { if (printPitLocalFileRadioBox->isChecked()) printPitLocalFileRadioBox->setChecked(false); } UpdateUtilitiesInterfaceAvailability(); } void MainWindow::LocalFilePrintPitToggled(bool checked) { if (checked) { if (printPitDeviceRadioBox->isChecked()) printPitDeviceRadioBox->setChecked(false); } UpdateUtilitiesInterfaceAvailability(); } void MainWindow::SelectPrintPitFile(void) { QString path = PromptFileSelection("Select PIT", "*.pit"); if (path.length() > 0) { printLocalPitLineEdit->setText(path); printPitButton->setEnabled(true); } else { printPitButton->setEnabled(false); } } void MainWindow::PrintPit(void) { utilityOutputPlainTextEdit->clear(); heimdallState = HeimdallState::PrintingPit | HeimdallState::NoReboot; heimdallFailed = false; QStringList arguments; arguments.append("print-pit"); if (printPitLocalFileRadioBox->isChecked()) { arguments.append("--file"); arguments.append(printLocalPitLineEdit->text()); } arguments.append("--stdout-errors"); arguments.append("--no-reboot"); if (resume) arguments.append("--resume"); if (verboseOutput) arguments.append("--verbose"); StartHeimdall(arguments); } void MainWindow::HandleHeimdallStdout(void) { QString output = heimdallProcess.readAll(); // We often receive multiple lots of output from Heimdall at one time. So we use regular expressions // to ensure we don't miss out on any important information. QRegExp uploadingExp("Uploading [^\n]+\n"); if (output.lastIndexOf(uploadingExp) > -1) flashLabel->setText(uploadingExp.cap().left(uploadingExp.cap().length() - 1)); QRegExp percentExp("[\b\n][0-9]+%"); if (output.lastIndexOf(percentExp) > -1) { QString percentString = percentExp.cap(); flashProgressBar->setValue(percentString.mid(1, percentString.length() - 2).toInt()); } output.remove(QChar('\b')); output.replace(QChar('%'), QString("%\n")); if (!!(heimdallState & HeimdallState::Flashing)) { outputPlainTextEdit->insertPlainText(output); outputPlainTextEdit->ensureCursorVisible(); } else { utilityOutputPlainTextEdit->insertPlainText(output); utilityOutputPlainTextEdit->ensureCursorVisible(); } } void MainWindow::HandleHeimdallReturned(int exitCode, QProcess::ExitStatus exitStatus) { HandleHeimdallStdout(); if (exitStatus == QProcess::NormalExit && exitCode == 0) { SetResume(!!(heimdallState & HeimdallState::NoReboot)); if (!!(heimdallState & HeimdallState::Flashing)) { flashLabel->setText("Flash completed successfully!"); } else if (!!(heimdallState & HeimdallState::DetectingDevice)) { deviceDetectedRadioButton->setChecked(true); } } else { if (!!(heimdallState & HeimdallState::Flashing)) { QString error = heimdallProcess.readAllStandardError(); int lastNewLineChar = error.lastIndexOf('\n'); if (lastNewLineChar == 0) error = error.mid(1).remove("ERROR: "); else error = error.left(lastNewLineChar).remove("ERROR: "); flashLabel->setText(error); } else if (!!(heimdallState & HeimdallState::DetectingDevice)) { deviceDetectedRadioButton->setChecked(false); } } heimdallState = HeimdallState::Stopped; flashProgressBar->setValue(0); flashProgressBar->setEnabled(false); UpdateInterfaceAvailability(); } void MainWindow::HandleHeimdallError(QProcess::ProcessError error) { if (error == QProcess::FailedToStart || error == QProcess::Timedout) { if (!!(heimdallState & HeimdallState::Flashing)) { flashLabel->setText("Failed to start Heimdall!"); flashProgressBar->setEnabled(false); } else { utilityOutputPlainTextEdit->setPlainText("\nFRONTEND ERROR: Failed to start Heimdall!"); } heimdallFailed = true; heimdallState = HeimdallState::Stopped; UpdateInterfaceAvailability(); } else if (error == QProcess::Crashed) { if (!!(heimdallState & HeimdallState::Flashing)) { flashLabel->setText("Heimdall crashed!"); flashProgressBar->setEnabled(false); } else { utilityOutputPlainTextEdit->appendPlainText("\nFRONTEND ERROR: Heimdall crashed!"); } heimdallState = HeimdallState::Stopped; UpdateInterfaceAvailability(); } else { if (!!(heimdallState & HeimdallState::Flashing)) { flashLabel->setText("Heimdall reported an unknown error!"); flashProgressBar->setEnabled(false); } else { utilityOutputPlainTextEdit->appendPlainText("\nFRONTEND ERROR: Heimdall reported an unknown error!"); } heimdallState = HeimdallState::Stopped; UpdateInterfaceAvailability(); } } Heimdall-v2.1.0/heimdall-frontend/source/mainwindow.h000066400000000000000000000125761464356164000226640ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef MAINWINDOW_H #define MAINWINDOW_H // Qt #include #include #include #include // libpit #include "libpit.h" // Heimdall Frontend #include "aboutform.h" #include "ui_mainwindow.h" #include "PackageData.h" using namespace libpit; namespace HeimdallFrontend { enum class HeimdallState : int { Stopped = 1, Flashing = (int)Stopped << 1, DetectingDevice = (int)Flashing << 1, ClosingPcScreen = (int)DetectingDevice << 1, PrintingPit = (int)ClosingPcScreen << 1, DownloadingPit = (int)PrintingPit << 1, NoReboot = (int)DownloadingPit << 1 }; inline HeimdallState operator|(HeimdallState lhs, HeimdallState rhs) { return (HeimdallState)((int)lhs | (int)rhs); } inline HeimdallState& operator|=(HeimdallState& lhs, HeimdallState rhs) { lhs = lhs | rhs; return lhs; } inline HeimdallState operator&(HeimdallState lhs, HeimdallState rhs) { lhs = (HeimdallState)((int)lhs & (int)rhs); return lhs; } inline bool operator!(HeimdallState state) { return (int)state == 0; } class MainWindow : public QMainWindow, public Ui::MainWindow { Q_OBJECT private: enum { kPrintPitSourceDevice = 0, kPrintPitSourceLocalFile }; AboutForm aboutForm; QString lastDirectory; int tabIndex; bool heimdallFailed; HeimdallState heimdallState; QProcess heimdallProcess; PackageData loadedPackageData; PitData currentPitData; PackageData workingPackageData; bool populatingPartitionNames; QList unusedPartitionIds; bool verboseOutput; bool resume; bool waitForDevice; void StartHeimdall(const QStringList& arguments); void UpdateUnusedPartitionIds(void); bool ReadPit(QFile *file); void UpdatePackageUserInterface(void); bool IsArchive(QString path); QString PromptFileSelection(const QString& caption = QString("Select File"), const QString& filter = QString()); QString PromptFileCreation(const QString& caption = QString("Save File"), const QString& filter = QString()); void UpdateLoadPackageInterfaceAvailability(void); void UpdateFlashInterfaceAvailability(void); void UpdateCreatePackageInterfaceAvailability(void); void UpdateUtilitiesInterfaceAvailability(void); void UpdateInterfaceAvailability(void); void UpdatePartitionNamesInterface(void); public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); public slots: void OpenDonationWebpage(void); void SetVerboseOutput(bool enabled); void ShowAbout(void); void FunctionTabChanged(int index); // Load Package Tab void SelectFirmwarePackage(void); void OpenDeveloperHomepage(void); void OpenDeveloperDonationWebpage(void); void LoadFirmwarePackage(void); // Flash Tab void SelectPartitionName(int index); void SelectPartitionFile(void); void SelectPartition(int row); void AddPartition(void); void RemovePartition(void); void SelectPit(void); void SetRepartition(int enabled); void SetNoReboot(int enabled); void SetResume(bool enabled); void SetResume(int enabled); void SetWaitForDevice(bool enabled); void SetWaitForDevice(int enabled); void StartFlash(void); // Create Package Tab void FirmwareNameChanged(const QString& text); void FirmwareVersionChanged(const QString& text); void PlatformNameChanged(const QString& text); void PlatformVersionChanged(const QString& text); void HomepageUrlChanged(const QString& text); void DonateUrlChanged(const QString& text); void DeveloperNameChanged(const QString& text); void SelectDeveloper(int row); void AddDeveloper(void); void RemoveDeveloper(void); void DeviceInfoChanged(const QString& text); void SelectDevice(int row); void AddDevice(void); void RemoveDevice(void); void BuildPackage(void); // Utilities Tab void DetectDevice(void); void ClosePcScreen(void); void SelectPitDestination(void); void DownloadPit(void); void DevicePrintPitToggled(bool checked); void LocalFilePrintPitToggled(bool checked); void SelectPrintPitFile(void); void PrintPit(void); // Heimdall Command Line void HandleHeimdallStdout(void); void HandleHeimdallReturned(int exitCode, QProcess::ExitStatus exitStatus); void HandleHeimdallError(QProcess::ProcessError error); }; } #endif // MAINWINDOW_H Heimdall-v2.1.0/heimdall/000077500000000000000000000000001464356164000152075ustar00rootroot00000000000000Heimdall-v2.1.0/heimdall/60-heimdall.rules000066400000000000000000000003551464356164000202700ustar00rootroot00000000000000SUBSYSTEM=="usb", ATTR{idVendor}=="04e8", ATTR{idProduct}=="6601", MODE="0666" SUBSYSTEM=="usb", ATTR{idVendor}=="04e8", ATTR{idProduct}=="685d", MODE="0666" SUBSYSTEM=="usb", ATTR{idVendor}=="04e8", ATTR{idProduct}=="68c3", MODE="0666" Heimdall-v2.1.0/heimdall/CMakeLists.txt000066400000000000000000000036611464356164000177550ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(heimdall) find_package(libusb REQUIRED) set(LIBPIT_INCLUDE_DIRS ../libpit/source) if(APPLE) set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lobjc -framework IOKit -framework CoreFoundation") endif() if(MINGW) set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -static") endif(MINGW) if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") add_definitions(-DOS_LINUX) endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") include_directories(SYSTEM ${LIBUSB_INCLUDE_DIRS}) include_directories(${LIBPIT_INCLUDE_DIRS}) set(HEIMDALL_SOURCE_FILES source/Arguments.cpp source/BridgeManager.cpp source/ClosePcScreenAction.cpp source/DetectAction.cpp source/DownloadPitAction.cpp source/FlashAction.cpp source/HelpAction.cpp source/InfoAction.cpp source/Interface.cpp source/main.cpp source/PrintPitAction.cpp source/Utility.cpp source/VersionAction.cpp) include(CheckSymbolExists) # # Large file support on UN*X, a/k/a LFS. # # On Windows, we require _fseeki64() and _ftelli64(). Visual Studio # has had supported them since Visual Studio 2005/MSVCR80. # if(NOT WIN32) include(FindLFS) if(LFS_FOUND) # # Add the required #defines. # add_definitions(${LFS_DEFINITIONS}) endif() # # Check for fseeko as well. # include(FindFseeko) if(FSEEKO_FOUND) set(HAVE_FSEEKO ON) # # Add the required #defines. # add_definitions(${FSEEKO_DEFINITIONS}) endif() endif() add_executable(heimdall ${HEIMDALL_SOURCE_FILES}) target_compile_features(heimdall PRIVATE cxx_std_11) target_link_libraries(heimdall PRIVATE pit) target_link_libraries(heimdall PRIVATE ${LIBUSB_LIBRARIES}) install (TARGETS heimdall RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) Heimdall-v2.1.0/heimdall/source/000077500000000000000000000000001464356164000165075ustar00rootroot00000000000000Heimdall-v2.1.0/heimdall/source/Arguments.cpp000066400000000000000000000135501464356164000211640ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Heimdall #include "Arguments.h" #include "Heimdall.h" #include "Interface.h" #include "Utility.h" using namespace std; using namespace Heimdall; FlagArgument *FlagArgument::ParseArgument(const std::string& name, __attribute__((unused)) int argc, __attribute__((unused)) char **argv, __attribute__((unused)) int& argi) { return new FlagArgument(name); } StringArgument *StringArgument::ParseArgument(const std::string& name, int argc, char **argv, int& argi) { if (++argi < argc) { return (new StringArgument(name, argv[argi])); } else { Interface::Print("Missing parameter for argument: %s\n\n", argv[argi - 1]); return (nullptr); } } UnsignedIntegerArgument *UnsignedIntegerArgument::ParseArgument(const std::string& name, int argc, char **argv, int& argi) { UnsignedIntegerArgument *unsignedIntegerArgument = nullptr; if (++argi < argc) { unsigned int value; if (Utility::ParseUnsignedInt(value, argv[argi]) == kNumberParsingStatusSuccess) unsignedIntegerArgument = new UnsignedIntegerArgument(name, value); else Interface::Print("%s must be a positive integer.", argv[argi - 1]); } else { Interface::Print("Missing parameter for argument: %s\n\n", argv[argi - 1]); } return (unsignedIntegerArgument); } Arguments::Arguments(const map& argumentTypes, const map& shortArgumentAliases, const map& argumentAliases) : argumentTypes(argumentTypes), shortArgumentAliases(shortArgumentAliases), argumentAliases(argumentAliases) { } Arguments::~Arguments() { for (vector::const_iterator it = argumentVector.begin(); it != argumentVector.end(); it++) delete *it; } bool Arguments::ParseArguments(int argc, char **argv, int argi) { for (; argi < argc; ++argi) { string argumentName = argv[argi]; string nonwildcardArgumentName; if (argumentName.find_first_of("--") == 0) { // Regular argument argumentName = argumentName.substr(2); nonwildcardArgumentName = argumentName; } else if (argumentName.find_first_of("-") == 0) { // Short argument alias string shortArgumentAlias = argumentName.substr(1); map::const_iterator shortAliasIt = shortArgumentAliases.find(shortArgumentAlias); if (shortAliasIt != shortArgumentAliases.end()) { argumentName = shortAliasIt->second; nonwildcardArgumentName = argumentName; } else { Interface::Print("Unknown argument: %s\n\n", argv[argi]); return (false); } } else { Interface::Print("Invalid argument: %s\n\n", argv[argi]); return (false); } map::const_iterator argumentTypeIt = argumentTypes.find(argumentName); if (argumentTypeIt == argumentTypes.end()) { // No argument with that name, maybe it's an alias... map::const_iterator aliasIt = argumentAliases.find(argumentName); if (aliasIt != argumentAliases.end()) { argumentName = aliasIt->second; nonwildcardArgumentName = argumentName; argumentTypeIt = argumentTypes.find(argumentName); } } // Handle wilcards unsigned int unsignedIntName; if (argumentTypeIt == argumentTypes.end()) { // Look for the unsigned integer wildcard "%d". if (Utility::ParseUnsignedInt(unsignedIntName, argumentName.c_str()) == kNumberParsingStatusSuccess) { argumentTypeIt = argumentTypes.find("%d"); argumentName = "%d"; } // Look for the string wildcard "%s" if (argumentTypeIt == argumentTypes.end()) { argumentTypeIt = argumentTypes.find("%s"); argumentName = "%s"; } } // We don't want to insert wild-cards into our argument map. if (argumentName == "%d" || argumentName == "%s") argumentName = nonwildcardArgumentName; Argument *argument = nullptr; if (argumentTypeIt != argumentTypes.end()) { switch (argumentTypeIt->second) { case kArgumentTypeFlag: argument = FlagArgument::ParseArgument(argumentName, argc, argv, argi); break; case kArgumentTypeString: argument = StringArgument::ParseArgument(argumentName, argc, argv, argi); break; case kArgumentTypeUnsignedInteger: argument = UnsignedIntegerArgument::ParseArgument(argumentName, argc, argv, argi); break; default: Interface::Print("Unknown argument type: %s\n\n", argv[argi]); break; } } else { Interface::Print("Unknown argument: %s\n\n", argv[argi]); } if (argument) { pair::iterator, bool> insertResult = argumentMap.insert(pair(argumentName, argument)); if (!insertResult.second) { Interface::Print("Duplicate argument: %s (%s)\n\n", argv[argi], argumentName.c_str()); delete argument; return (false); } argumentVector.push_back(argument); } else { return (false); } } return (true); } Heimdall-v2.1.0/heimdall/source/Arguments.h000066400000000000000000000100131464356164000206200ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef ARGUMENTS_H #define ARGUMENTS_H // C/C++ Standard Library #include #include #include // Heimdall #include "Heimdall.h" namespace Heimdall { typedef enum { kArgumentTypeFlag = 0, kArgumentTypeString, kArgumentTypeUnsignedInteger } ArgumentType; class Argument { private: std::string name; ArgumentType type; protected: Argument(const std::string& name, ArgumentType type) { this->name = name; this->type = type; } public: virtual ~Argument() { } const std::string& GetName(void) const { return name; } ArgumentType GetType(void) const { return type; } }; class FlagArgument : public Argument { private: FlagArgument(const std::string& name) : Argument(name, kArgumentTypeFlag) { } public: static FlagArgument *ParseArgument(const std::string& name, int argc, char **argv, int& argi); }; class StringArgument : public Argument { private: std::string value; StringArgument(const std::string& name, const std::string& value) : Argument(name, kArgumentTypeString) { this->value = value; } public: static StringArgument *ParseArgument(const std::string& name, int argc, char **argv, int& argi); const std::string& GetValue(void) const { return (value); } }; class UnsignedIntegerArgument : public Argument { private: unsigned int value; UnsignedIntegerArgument(const std::string& name, unsigned int value) : Argument(name, kArgumentTypeUnsignedInteger) { this->value = value; } public: static UnsignedIntegerArgument *ParseArgument(const std::string& name, int argc, char **argv, int& argi); unsigned int GetValue(void) const { return (value); } }; class Arguments { private: const std::map argumentTypes; const std::map shortArgumentAliases; const std::map argumentAliases; std::vector argumentVector; std::map argumentMap; public: Arguments(const std::map& argumentTypes, const std::map& shortArgumentAliases = (std::map()), const std::map& argumentAliases = (std::map())); ~Arguments(); // argi is the index of the first argument to parse. bool ParseArguments(int argc, char **argv, int argi); const Argument *GetArgument(std::string argumentName) const { std::map::const_iterator it = argumentMap.find(argumentName); return (it != argumentMap.end() ? it->second : nullptr); } const std::map& GetArgumentTypes(void) const { return (argumentTypes); } const std::vector& GetArguments(void) const { return (argumentVector); } }; } #endif Heimdall-v2.1.0/heimdall/source/BeginDumpPacket.h000066400000000000000000000036471464356164000216740ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef BEGINDUMPPACKET_H #define BEGINDUMPPACKET_H // Heimdall #include "FileTransferPacket.h" namespace Heimdall { class BeginDumpPacket : public FileTransferPacket { public: enum { kChipTypeRam = 0, kChipTypeNand = 1 }; private: unsigned int chipType; unsigned int chipId; public: BeginDumpPacket(unsigned int chipType, unsigned int chipId) : FileTransferPacket(FileTransferPacket::kRequestDump) { this->chipType = chipType; this->chipId = chipId; } unsigned int GetChipType(void) const { return (chipType); } unsigned int GetChipId(void) const { return (chipId); } virtual void Pack(void) { FileTransferPacket::Pack(); PackInteger(FileTransferPacket::kDataSize, chipType); PackInteger(FileTransferPacket::kDataSize + 4, chipId); } }; } #endif Heimdall-v2.1.0/heimdall/source/BeginSessionPacket.h000066400000000000000000000031411464356164000223770ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef BEGINSESSIONPACKET_H #define BEGINSESSIONPACKET_H // Heimdall #include "SessionSetupPacket.h" namespace Heimdall { class BeginSessionPacket : public SessionSetupPacket { protected: enum { kDataSize = SessionSetupPacket::kDataSize + 4 }; public: BeginSessionPacket() : SessionSetupPacket(SessionSetupPacket::kBeginSession) { } void Pack(void) { SessionSetupPacket::Pack(); /* Odin protocol version. */ PackInteger(SessionSetupPacket::kDataSize, 0x4); } }; } #endif Heimdall-v2.1.0/heimdall/source/BridgeManager.cpp000066400000000000000000001047021464356164000217060ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // C Standard Library #include #include // libusb #include // Heimdall #include "BeginDumpPacket.h" #include "BeginSessionPacket.h" #include "BridgeManager.h" #include "DeviceTypePacket.h" #include "DumpPartFileTransferPacket.h" #include "DumpPartPitFilePacket.h" #include "DumpResponse.h" #include "EndModemFileTransferPacket.h" #include "EndPhoneFileTransferPacket.h" #include "EndPitFileTransferPacket.h" #include "EndSessionPacket.h" #include "FilePartSizePacket.h" #include "FileTransferPacket.h" #include "FlashPartFileTransferPacket.h" #include "FlashPartPitFilePacket.h" #include "InboundPacket.h" #include "Interface.h" #include "OutboundPacket.h" #include "PitFilePacket.h" #include "PitFileResponse.h" #include "ReceiveFilePartPacket.h" #include "ResponsePacket.h" #include "SendFilePartPacket.h" #include "SendFilePartResponse.h" #include "SessionSetupPacket.h" #include "SessionSetupResponse.h" // Future versions of libusb will use usb_interface instead of interface. #ifndef usb_interface #define usb_interface interface #endif #define USB_CLASS_CDC_DATA 0x0A using namespace libpit; using namespace Heimdall; const DeviceIdentifier BridgeManager::supportedDevices[BridgeManager::kSupportedDeviceCount] = { DeviceIdentifier(BridgeManager::kVidSamsung, BridgeManager::kPidGalaxyS), DeviceIdentifier(BridgeManager::kVidSamsung, BridgeManager::kPidGalaxyS2), DeviceIdentifier(BridgeManager::kVidSamsung, BridgeManager::kPidDroidCharge) }; enum { kFileTransferSequenceMaxLengthDefault = 800, kFileTransferPacketSizeDefault = 131072, kFileTransferSequenceTimeoutDefault = 30000 // 30 seconds }; int BridgeManager::FindDeviceInterface(void) { if (waitForDevice) Interface::Print("Waiting for device...\n"); else Interface::Print("Detecting device...\n"); struct libusb_device **devices; unsigned int deviceCount, deviceIndex, i; libusb_device_descriptor descriptor; while (true) { deviceCount = libusb_get_device_list(libusbContext, &devices); for (deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) { libusb_get_device_descriptor(devices[deviceIndex], &descriptor); for (i = 0; i < BridgeManager::kSupportedDeviceCount; i++) { if (descriptor.idVendor == supportedDevices[i].vendorId && descriptor.idProduct == supportedDevices[i].productId) { heimdallDevice = devices[deviceIndex]; libusb_ref_device(heimdallDevice); break; } } } if (heimdallDevice) break; libusb_free_device_list(devices, deviceCount); if (waitForDevice) Sleep(1000); else break; } if (!heimdallDevice) { Interface::PrintDeviceDetectionFailed(); return (BridgeManager::kInitialiseDeviceNotDetected); } int result = libusb_open(heimdallDevice, &deviceHandle); if (result != LIBUSB_SUCCESS) { Interface::PrintError("Failed to access device. libusb error: %d\n", result); return (BridgeManager::kInitialiseFailed); } libusb_device_descriptor deviceDescriptor; result = libusb_get_device_descriptor(heimdallDevice, &deviceDescriptor); if (result != LIBUSB_SUCCESS) { Interface::PrintError("Failed to retrieve device description\n"); return (BridgeManager::kInitialiseFailed); } if (verbose) { unsigned char stringBuffer[128]; if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iManufacturer, stringBuffer, 128) >= 0) { Interface::Print(" Manufacturer: \"%s\"\n", stringBuffer); } if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iProduct, stringBuffer, 128) >= 0) { Interface::Print(" Product: \"%s\"\n", stringBuffer); } if (libusb_get_string_descriptor_ascii(deviceHandle, deviceDescriptor.iSerialNumber, stringBuffer, 128) >= 0) { Interface::Print(" Serial No: \"%s\"\n", stringBuffer); } Interface::Print("\n length: %d\n", deviceDescriptor.bLength); Interface::Print(" device class: %d\n", deviceDescriptor.bDeviceClass); Interface::Print(" S/N: %d\n", deviceDescriptor.iSerialNumber); Interface::Print(" VID:PID: %04X:%04X\n", deviceDescriptor.idVendor, deviceDescriptor.idProduct); Interface::Print(" bcdDevice: %04X\n", deviceDescriptor.bcdDevice); Interface::Print(" iMan:iProd:iSer: %d:%d:%d\n", deviceDescriptor.iManufacturer, deviceDescriptor.iProduct, deviceDescriptor.iSerialNumber); Interface::Print(" nb confs: %d\n", deviceDescriptor.bNumConfigurations); } libusb_config_descriptor *configDescriptor; result = libusb_get_config_descriptor(heimdallDevice, 0, &configDescriptor); if (result != LIBUSB_SUCCESS || !configDescriptor) { Interface::PrintError("Failed to retrieve config descriptor\n"); return (BridgeManager::kInitialiseFailed); } interfaceIndex = -1; altSettingIndex = -1; for (int i = 0; i < configDescriptor->bNumInterfaces; i++) { for (int j = 0 ; j < configDescriptor->usb_interface[i].num_altsetting; j++) { if (verbose) { Interface::Print("\ninterface[%d].altsetting[%d]: num endpoints = %d\n", i, j, configDescriptor->usb_interface[i].altsetting[j].bNumEndpoints); Interface::Print(" Class.SubClass.Protocol: %02X.%02X.%02X\n", configDescriptor->usb_interface[i].altsetting[j].bInterfaceClass, configDescriptor->usb_interface[i].altsetting[j].bInterfaceSubClass, configDescriptor->usb_interface[i].altsetting[j].bInterfaceProtocol); } int inEndpointAddress = -1; int outEndpointAddress = -1; for (int k = 0; k < configDescriptor->usb_interface[i].altsetting[j].bNumEndpoints; k++) { const libusb_endpoint_descriptor *endpoint = &configDescriptor->usb_interface[i].altsetting[j].endpoint[k]; if (verbose) { Interface::Print(" endpoint[%d].address: %02X\n", k, endpoint->bEndpointAddress); Interface::Print(" max packet size: %04X\n", endpoint->wMaxPacketSize); Interface::Print(" polling interval: %02X\n", endpoint->bInterval); } if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) inEndpointAddress = endpoint->bEndpointAddress; else outEndpointAddress = endpoint->bEndpointAddress; } if (interfaceIndex < 0 && configDescriptor->usb_interface[i].altsetting[j].bNumEndpoints == 2 && configDescriptor->usb_interface[i].altsetting[j].bInterfaceClass == USB_CLASS_CDC_DATA && inEndpointAddress != -1 && outEndpointAddress != -1) { interfaceIndex = i; altSettingIndex = j; inEndpoint = inEndpointAddress; outEndpoint = outEndpointAddress; } } } libusb_free_config_descriptor(configDescriptor); if (interfaceIndex < 0) { Interface::PrintError("Failed to find correct interface configuration\n"); return (BridgeManager::kInitialiseFailed); } return (BridgeManager::kInitialiseSucceeded); } bool BridgeManager::ClaimDeviceInterface(void) { Interface::Print("Claiming interface...\n"); int result = libusb_claim_interface(deviceHandle, interfaceIndex); #ifdef OS_LINUX if (result != LIBUSB_SUCCESS) { detachedDriver = true; Interface::Print("Attempt failed. Detaching driver...\n"); libusb_detach_kernel_driver(deviceHandle, interfaceIndex); Interface::Print("Claiming interface again...\n"); result = libusb_claim_interface(deviceHandle, interfaceIndex); } #endif if (result != LIBUSB_SUCCESS) { Interface::PrintError("Claiming interface failed!\n"); return (false); } interfaceClaimed = true; return (true); } bool BridgeManager::SetupDeviceInterface(void) { // if altSettingIndex is 0 there should be no need // to set alt_setting(?) if (altSettingIndex == 0) return (true); Interface::Print("Setting up interface...\n"); int result = libusb_set_interface_alt_setting(deviceHandle, interfaceIndex, altSettingIndex); if (result != LIBUSB_SUCCESS) { Interface::PrintError("Setting up interface failed!\n"); return (false); } Interface::Print("\n"); return (true); } void BridgeManager::ReleaseDeviceInterface(void) { Interface::Print("Releasing device interface...\n"); libusb_release_interface(deviceHandle, interfaceIndex); #ifdef OS_LINUX if (detachedDriver) { Interface::Print("Re-attaching kernel driver...\n"); libusb_attach_kernel_driver(deviceHandle, interfaceIndex); } #endif interfaceClaimed = false; Interface::Print("\n"); } bool BridgeManager::InitialiseProtocol(void) { Interface::Print("Initialising protocol...\n"); unsigned char dataBuffer[7]; // Send "ODIN" memcpy(dataBuffer, "ODIN", 4); memset(dataBuffer + 4, 0, 1); #ifdef OS_LINUX if (IsUbuntu()) { Interface::Print("Resetting device...\n"); if (libusb_reset_device(deviceHandle)) { Interface::PrintError("Failed to reset device!\n"); } } #endif if (!SendBulkTransfer(dataBuffer, 4, 1000)) { Interface::PrintError("Failed to send handshake!\n"); } // Expect "LOKE" memset(dataBuffer, 0, 7); int dataTransferred = 0; int result = libusb_bulk_transfer(deviceHandle, inEndpoint, dataBuffer, 7, &dataTransferred, 1000); if (result != LIBUSB_SUCCESS) { if (verbose) Interface::PrintError("Failed to receive handshake response. Result: %d\n", result); } else { if (dataTransferred == 4 && memcmp(dataBuffer, "LOKE", 4) == 0) { // Successfully received "LOKE" Interface::Print("Protocol initialisation successful.\n\n"); return (true); } else { if (verbose) Interface::PrintError("Expected: \"LOKE\"\nReceived: \"%s\"\n", dataBuffer); Interface::PrintError("Unexpected handshake response!\n"); } } Interface::PrintError("Protocol initialisation failed!\n\n"); return (false); } BridgeManager::BridgeManager(bool verbose, bool waitForDevice) { this->verbose = verbose; this->waitForDevice = waitForDevice; libusbContext = nullptr; deviceHandle = nullptr; heimdallDevice = nullptr; inEndpoint = -1; outEndpoint = -1; interfaceIndex = -1; altSettingIndex = -1; interfaceClaimed = false; #ifdef OS_LINUX detachedDriver = false; #endif fileTransferSequenceMaxLength = kFileTransferSequenceMaxLengthDefault; fileTransferPacketSize = kFileTransferPacketSizeDefault; fileTransferSequenceTimeout = kFileTransferSequenceTimeoutDefault; usbLogLevel = UsbLogLevel::Default; } BridgeManager::~BridgeManager() { if (interfaceClaimed) ReleaseDeviceInterface(); if (deviceHandle) libusb_close(deviceHandle); if (heimdallDevice) libusb_unref_device(heimdallDevice); if (libusbContext) libusb_exit(libusbContext); } bool BridgeManager::DetectDevice(void) { // Initialise libusb int result = libusb_init(&libusbContext); if (result != LIBUSB_SUCCESS) { Interface::PrintError("Failed to initialise libusb. libusb error: %d\n", result); return (false); } if (waitForDevice) Interface::Print("Waiting for device...\n"); // Set libusb log level. SetUsbLogLevel(usbLogLevel); struct libusb_device **devices; unsigned int deviceCount, deviceIndex, i; libusb_device_descriptor descriptor; while (true) { deviceCount = libusb_get_device_list(libusbContext, &devices); for (deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) { libusb_get_device_descriptor(devices[deviceIndex], &descriptor); for (i = 0; i < BridgeManager::kSupportedDeviceCount; i++) { if (descriptor.idVendor == supportedDevices[i].vendorId && descriptor.idProduct == supportedDevices[i].productId) { libusb_free_device_list(devices, deviceCount); Interface::Print("Device detected\n"); return (true); } } } libusb_free_device_list(devices, deviceCount); if (waitForDevice) Sleep(1000); else break; } Interface::PrintDeviceDetectionFailed(); return (false); } int BridgeManager::Initialise(bool resume) { Interface::Print("Initialising connection...\n"); // Initialise libusb int result = libusb_init(&libusbContext); if (result != LIBUSB_SUCCESS) { Interface::PrintError("Failed to initialise libusb. libusb error: %d\n", result); Interface::Print("Failed to connect to device!\n"); return (BridgeManager::kInitialiseFailed); } // Setup libusb log level. SetUsbLogLevel(usbLogLevel); result = FindDeviceInterface(); if (result != BridgeManager::kInitialiseSucceeded) return (result); if (!ClaimDeviceInterface()) return (BridgeManager::kInitialiseFailed); if (!SetupDeviceInterface()) return (BridgeManager::kInitialiseFailed); if (!resume) { if (!InitialiseProtocol()) return (BridgeManager::kInitialiseFailed); } return (BridgeManager::kInitialiseSucceeded); } bool BridgeManager::BeginSession(void) { Interface::Print("Beginning session...\n"); BeginSessionPacket beginSessionPacket; if (!SendPacket(&beginSessionPacket)) { Interface::PrintError("Failed to begin session!\n"); return (false); } SessionSetupResponse beginSessionResponse; if (!ReceivePacket(&beginSessionResponse)) return (false); unsigned int deviceDefaultPacketSize = beginSessionResponse.GetResult(); Interface::Print("\nSome devices may take up to 2 minutes to respond.\nPlease be patient!\n\n"); Sleep(3000); // Give the user time to read the message. if (deviceDefaultPacketSize != 0) // 0 means changing the packet size is not supported. { fileTransferSequenceTimeout = 120000; // 2 minutes! fileTransferPacketSize = 1048576; // 1 MiB fileTransferSequenceMaxLength = 30; // Therefore, fileTransferPacketSize * fileTransferSequenceMaxLength == 30 MiB per sequence. FilePartSizePacket filePartSizePacket(fileTransferPacketSize); if (!SendPacket(&filePartSizePacket)) { Interface::PrintError("Failed to send file part size packet!\n"); return (false); } SessionSetupResponse filePartSizeResponse; if (!ReceivePacket(&filePartSizeResponse)) return (false); if (filePartSizeResponse.GetResult() != 0) { Interface::PrintError("Unexpected file part size response!\nExpected: 0\nReceived: %d\n", filePartSizeResponse.GetResult()); return (false); } } Interface::Print("Session begun.\n\n"); return (true); } bool BridgeManager::EndSession(bool reboot) const { Interface::Print("Ending session...\n"); EndSessionPacket *endSessionPacket = new EndSessionPacket(EndSessionPacket::kRequestEndSession); bool success = SendPacket(endSessionPacket); delete endSessionPacket; if (!success) { Interface::PrintError("Failed to send end session packet!\n"); return (false); } ResponsePacket *endSessionResponse = new ResponsePacket(ResponsePacket::kResponseTypeEndSession); success = ReceivePacket(endSessionResponse); delete endSessionResponse; if (!success) { Interface::PrintError("Failed to receive session end confirmation!\n"); return (false); } if (reboot) { Interface::Print("Rebooting device...\n"); EndSessionPacket *rebootDevicePacket = new EndSessionPacket(EndSessionPacket::kRequestRebootDevice); bool success = SendPacket(rebootDevicePacket); delete rebootDevicePacket; if (!success) { Interface::PrintError("Failed to send reboot device packet!\n"); return (false); } ResponsePacket *rebootDeviceResponse = new ResponsePacket(ResponsePacket::kResponseTypeEndSession); success = ReceivePacket(rebootDeviceResponse); delete rebootDeviceResponse; if (!success) { Interface::PrintError("Failed to receive reboot confirmation!\n"); return (false); } } return (true); } bool BridgeManager::SendBulkTransfer(unsigned char *data, int length, int timeout, bool retry) const { int dataTransferred; int result = libusb_bulk_transfer(deviceHandle, outEndpoint, data, length, &dataTransferred, timeout); if (result != LIBUSB_SUCCESS && retry) { static const int retryDelay = 250; if (verbose) Interface::PrintError("libusb error %d whilst sending bulk transfer.", result); // Retry for (int i = 0; i < 5; i++) { if (verbose) Interface::PrintErrorSameLine(" Retrying...\n"); // Wait longer each retry Sleep(retryDelay * (i + 1)); result = libusb_bulk_transfer(deviceHandle, outEndpoint, data, length, &dataTransferred, timeout); if (result == LIBUSB_SUCCESS) break; if (verbose) Interface::PrintError("libusb error %d whilst sending bulk transfer.", result); } if (verbose) Interface::PrintErrorSameLine("\n"); } return (result == LIBUSB_SUCCESS && dataTransferred == length); } int BridgeManager::ReceiveBulkTransfer(unsigned char *data, int length, int timeout, bool retry) const { if (data == nullptr) { // HACK: It seems WinUSB ignores us when we try to read with length zero. static unsigned char dummyData; data = &dummyData; length = 1; } int dataTransferred; int result = libusb_bulk_transfer(deviceHandle, inEndpoint, data, length, &dataTransferred, timeout); if (result != LIBUSB_SUCCESS && retry) { static const int retryDelay = 250; if (verbose) Interface::PrintError("libusb error %d whilst receiving bulk transfer.", result); // Retry for (int i = 0; i < 5; i++) { if (verbose) Interface::PrintErrorSameLine(" Retrying...\n"); // Wait longer each retry Sleep(retryDelay * (i + 1)); result = libusb_bulk_transfer(deviceHandle, inEndpoint, data, length, &dataTransferred, timeout); if (result == LIBUSB_SUCCESS) break; if (verbose) Interface::PrintError("libusb error %d whilst receiving bulk transfer.", result); } if (verbose) Interface::PrintErrorSameLine("\n"); } if (result != LIBUSB_SUCCESS) return (result); return (dataTransferred); } bool BridgeManager::SendPacket(OutboundPacket *packet, int timeout, int emptyTransferFlags) const { packet->Pack(); if (emptyTransferFlags & kEmptyTransferBefore) { if (!SendBulkTransfer(nullptr, 0, kDefaultTimeoutEmptyTransfer, false) && verbose) { Interface::PrintWarning("Empty bulk transfer before sending packet failed. Continuing anyway...\n"); } } if (!SendBulkTransfer(packet->GetData(), packet->GetSize(), timeout)) return (false); if (emptyTransferFlags & kEmptyTransferAfter) { if (!SendBulkTransfer(nullptr, 0, kDefaultTimeoutEmptyTransfer, false) && verbose) { Interface::PrintWarning("Empty bulk transfer after sending packet failed. Continuing anyway...\n"); } } return (true); } bool BridgeManager::ReceivePacket(InboundPacket *packet, int timeout, int emptyTransferFlags) const { if (emptyTransferFlags & kEmptyTransferBefore) { if (ReceiveBulkTransfer(nullptr, 0, kDefaultTimeoutEmptyTransfer, false) < 0 && verbose) { Interface::PrintWarning("Empty bulk transfer before receiving packet failed. Continuing anyway...\n"); } } int receivedSize = ReceiveBulkTransfer(packet->GetData(), packet->GetSize(), timeout); if (receivedSize < 0) return (false); if (static_cast(receivedSize) != packet->GetSize() && !packet->IsSizeVariable()) { if (verbose) Interface::PrintError("Incorrect packet size received - expected size = %d, received size = %d.\n", packet->GetSize(), receivedSize); return (false); } packet->SetReceivedSize(receivedSize); bool unpacked = packet->Unpack(); if (!unpacked && verbose) Interface::PrintError("Failed to unpack received packet.\n"); if (emptyTransferFlags & kEmptyTransferAfter) { if (ReceiveBulkTransfer(nullptr, 0, kDefaultTimeoutEmptyTransfer, false) < 0 && verbose) { Interface::PrintWarning("Empty bulk transfer after receiving packet failed. Continuing anyway...\n"); } } return (unpacked); } bool BridgeManager::RequestDeviceType(unsigned int request, int *result) const { DeviceTypePacket deviceTypePacket; bool success = SendPacket(&deviceTypePacket); if (!success) { Interface::PrintError("Failed to request device info packet!\n"); if (verbose) Interface::PrintError("Failed request: %u\n", request); return (false); } SessionSetupResponse deviceTypeResponse; if (!ReceivePacket(&deviceTypeResponse)) return (false); *result = deviceTypeResponse.GetResult(); return (true); } bool BridgeManager::SendPitData(const PitData *pitData) const { unsigned int pitBufferSize = pitData->GetPaddedSize(); // Start file transfer PitFilePacket *pitFilePacket = new PitFilePacket(PitFilePacket::kRequestFlash); bool success = SendPacket(pitFilePacket); delete pitFilePacket; if (!success) { Interface::PrintError("Failed to initialise PIT file transfer!\n"); return (false); } PitFileResponse *pitFileResponse = new PitFileResponse(); success = ReceivePacket(pitFileResponse); delete pitFileResponse; if (!success) { Interface::PrintError("Failed to confirm transfer initialisation!\n"); return (false); } // Transfer file size FlashPartPitFilePacket *flashPartPitFilePacket = new FlashPartPitFilePacket(pitBufferSize); success = SendPacket(flashPartPitFilePacket); delete flashPartPitFilePacket; if (!success) { Interface::PrintError("Failed to send PIT file part information!\n"); return (false); } pitFileResponse = new PitFileResponse(); success = ReceivePacket(pitFileResponse); delete pitFileResponse; if (!success) { Interface::PrintError("Failed to confirm sending of PIT file part information!\n"); return (false); } // Create packed in-memory PIT file unsigned char *pitBuffer = new unsigned char[pitBufferSize]; memset(pitBuffer, 0, pitBufferSize); pitData->Pack(pitBuffer); // Flash pit file SendFilePartPacket *sendFilePartPacket = new SendFilePartPacket(pitBuffer, pitBufferSize); success = SendPacket(sendFilePartPacket); delete sendFilePartPacket; delete [] pitBuffer; if (!success) { Interface::PrintError("Failed to send file part packet!\n"); return (false); } pitFileResponse = new PitFileResponse(); success = ReceivePacket(pitFileResponse); delete pitFileResponse; if (!success) { Interface::PrintError("Failed to receive PIT file part response!\n"); return (false); } // End pit file transfer EndPitFileTransferPacket *endPitFileTransferPacket = new EndPitFileTransferPacket(pitBufferSize); success = SendPacket(endPitFileTransferPacket); delete endPitFileTransferPacket; if (!success) { Interface::PrintError("Failed to send end PIT file transfer packet!\n"); return (false); } pitFileResponse = new PitFileResponse(); success = ReceivePacket(pitFileResponse); delete pitFileResponse; if (!success) { Interface::PrintError("Failed to confirm end of PIT file transfer!\n"); return (false); } return (true); } int BridgeManager::ReceivePitFile(unsigned char **pitBuffer) const { *pitBuffer = nullptr; bool success; // Start file transfer PitFilePacket *pitFilePacket = new PitFilePacket(PitFilePacket::kRequestDump); success = SendPacket(pitFilePacket); delete pitFilePacket; if (!success) { Interface::PrintError("Failed to request receival of PIT file!\n"); return (0); } PitFileResponse *pitFileResponse = new PitFileResponse(); success = ReceivePacket(pitFileResponse); unsigned int fileSize = pitFileResponse->GetFileSize(); delete pitFileResponse; if (!success) { Interface::PrintError("Failed to receive PIT file size!\n"); return (0); } unsigned int transferCount = fileSize / ReceiveFilePartPacket::kDataSize; if (fileSize % ReceiveFilePartPacket::kDataSize != 0) transferCount++; unsigned char *buffer = new unsigned char[fileSize]; int offset = 0; for (unsigned int i = 0; i < transferCount; i++) { DumpPartPitFilePacket *requestPacket = new DumpPartPitFilePacket(i); success = SendPacket(requestPacket); delete requestPacket; if (!success) { Interface::PrintError("Failed to request PIT file part #%d!\n", i); delete [] buffer; return (0); } int receiveEmptyTransferFlags = (i == transferCount - 1) ? kEmptyTransferAfter : kEmptyTransferNone; ReceiveFilePartPacket *receiveFilePartPacket = new ReceiveFilePartPacket(); success = ReceivePacket(receiveFilePartPacket, kDefaultTimeoutReceive, receiveEmptyTransferFlags); if (!success) { Interface::PrintError("Failed to receive PIT file part #%d!\n", i); delete receiveFilePartPacket; delete [] buffer; return (0); } // Copy the whole packet data into the buffer. memcpy(buffer + offset, receiveFilePartPacket->GetData(), receiveFilePartPacket->GetReceivedSize()); offset += receiveFilePartPacket->GetReceivedSize(); delete receiveFilePartPacket; } // End file transfer pitFilePacket = new PitFilePacket(PitFilePacket::kRequestEndTransfer); success = SendPacket(pitFilePacket); delete pitFilePacket; if (!success) { Interface::PrintError("Failed to send request to end PIT file transfer!\n"); delete [] buffer; return (0); } pitFileResponse = new PitFileResponse(); success = ReceivePacket(pitFileResponse); delete pitFileResponse; if (!success) { Interface::PrintError("Failed to receive end PIT file transfer verification!\n"); delete [] buffer; return (0); } *pitBuffer = buffer; return (fileSize); } int BridgeManager::DownloadPitFile(unsigned char **pitBuffer) const { Interface::Print("Downloading device's PIT file...\n"); int devicePitFileSize = ReceivePitFile(pitBuffer); if (!*pitBuffer) { Interface::PrintError("Failed to download PIT file!\n"); return (0); } Interface::Print("PIT file download successful.\n\n"); return (devicePitFileSize); } bool BridgeManager::SendFile(FILE *file, unsigned int destination, unsigned int deviceType, unsigned int fileIdentifier) const { if (destination != EndFileTransferPacket::kDestinationModem && destination != EndFileTransferPacket::kDestinationPhone) { Interface::PrintError("Attempted to send file to unknown destination!\n"); return (false); } if (destination == EndFileTransferPacket::kDestinationModem && fileIdentifier != 0xFFFFFFFF) { Interface::PrintError("The modem file does not have an identifier!\n"); return (false); } FileTransferPacket *flashFileTransferPacket = new FileTransferPacket(FileTransferPacket::kRequestFlash); bool success = SendPacket(flashFileTransferPacket); delete flashFileTransferPacket; if (!success) { Interface::PrintError("Failed to initialise file transfer!\n"); return (false); } FileSeek(file, 0, SEEK_END); unsigned long fileSize = (unsigned long)FileTell(file); FileRewind(file); ResponsePacket *fileTransferResponse = new ResponsePacket(ResponsePacket::kResponseTypeFileTransfer); success = ReceivePacket(fileTransferResponse); delete fileTransferResponse; if (!success) { Interface::PrintError("Failed to confirm transfer initialisation!\n"); return (false); } unsigned int sequenceCount = fileSize / (fileTransferSequenceMaxLength * fileTransferPacketSize); unsigned int lastSequenceSize = fileTransferSequenceMaxLength; unsigned int partialPacketByteCount = fileSize % fileTransferPacketSize; if (fileSize % (fileTransferSequenceMaxLength * fileTransferPacketSize) != 0) { sequenceCount++; unsigned int lastSequenceBytes = fileSize % (fileTransferSequenceMaxLength * fileTransferPacketSize); lastSequenceSize = lastSequenceBytes / fileTransferPacketSize; if (partialPacketByteCount != 0) lastSequenceSize++; } unsigned long bytesTransferred = 0; unsigned int currentPercent; unsigned int previousPercent = 0; Interface::Print("0%%"); for (unsigned int sequenceIndex = 0; sequenceIndex < sequenceCount; sequenceIndex++) { bool isLastSequence = (sequenceIndex == sequenceCount - 1); unsigned int sequenceSize = (isLastSequence) ? lastSequenceSize : fileTransferSequenceMaxLength; unsigned int sequenceTotalByteCount = sequenceSize * fileTransferPacketSize; FlashPartFileTransferPacket *beginFileTransferPacket = new FlashPartFileTransferPacket(sequenceTotalByteCount); success = SendPacket(beginFileTransferPacket); delete beginFileTransferPacket; if (!success) { Interface::PrintErrorSameLine("\n"); Interface::PrintError("Failed to begin file transfer sequence!\n"); return (false); } fileTransferResponse = new ResponsePacket(ResponsePacket::kResponseTypeFileTransfer); bool success = ReceivePacket(fileTransferResponse); delete fileTransferResponse; if (!success) { Interface::PrintErrorSameLine("\n"); Interface::PrintError("Failed to confirm beginning of file transfer sequence!\n"); return (false); } for (unsigned int filePartIndex = 0; filePartIndex < sequenceSize; filePartIndex++) { // NOTE: This empty transfer thing is entirely ridiculous, but sadly it seems to be required. int sendEmptyTransferFlags = (filePartIndex == 0) ? kEmptyTransferNone : kEmptyTransferBefore; // Send SendFilePartPacket *sendFilePartPacket = new SendFilePartPacket(file, fileTransferPacketSize); success = SendPacket(sendFilePartPacket, kDefaultTimeoutSend, sendEmptyTransferFlags); delete sendFilePartPacket; if (!success) { Interface::PrintErrorSameLine("\n"); Interface::PrintError("Failed to send file part packet!\n"); return (false); } // Response SendFilePartResponse *sendFilePartResponse = new SendFilePartResponse(); success = ReceivePacket(sendFilePartResponse); unsigned int receivedPartIndex = sendFilePartResponse->GetPartIndex(); delete sendFilePartResponse; if (!success) { Interface::PrintErrorSameLine("\n"); Interface::PrintError("Failed to receive file part response!\n"); for (int retry = 0; retry < 4; retry++) { Interface::PrintErrorSameLine("\n"); Interface::PrintError("Retrying..."); // Send sendFilePartPacket = new SendFilePartPacket(file, fileTransferPacketSize); success = SendPacket(sendFilePartPacket, kDefaultTimeoutSend, sendEmptyTransferFlags); delete sendFilePartPacket; if (!success) { Interface::PrintErrorSameLine("\n"); Interface::PrintError("Failed to send file part packet!\n"); return (false); } // Response sendFilePartResponse = new SendFilePartResponse(); success = ReceivePacket(sendFilePartResponse); unsigned int receivedPartIndex = sendFilePartResponse->GetPartIndex(); delete sendFilePartResponse; if (receivedPartIndex != filePartIndex) { Interface::PrintErrorSameLine("\n"); Interface::PrintError("Expected file part index: %d Received: %d\n", filePartIndex, receivedPartIndex); return (false); } if (success) break; } if (!success) return (false); } if (receivedPartIndex != filePartIndex) { Interface::PrintErrorSameLine("\n"); Interface::PrintError("Expected file part index: %d Received: %d\n", filePartIndex, receivedPartIndex); return (false); } bytesTransferred += fileTransferPacketSize; if (bytesTransferred > fileSize) bytesTransferred = fileSize; currentPercent = (unsigned int)(100.0 * ((double)bytesTransferred / (double)fileSize)); if (currentPercent != previousPercent) { if (!verbose) { if (previousPercent < 10) Interface::Print("\b\b%d%%", currentPercent); else Interface::Print("\b\b\b%d%%", currentPercent); } else { Interface::Print("\n%d%%\n", currentPercent); } } previousPercent = currentPercent; } unsigned int sequenceEffectiveByteCount = (isLastSequence && partialPacketByteCount != 0) ? fileTransferPacketSize * (lastSequenceSize - 1) + partialPacketByteCount : sequenceTotalByteCount; if (destination == EndFileTransferPacket::kDestinationPhone) { EndPhoneFileTransferPacket *endPhoneFileTransferPacket = new EndPhoneFileTransferPacket(sequenceEffectiveByteCount, 0, deviceType, fileIdentifier, isLastSequence); success = SendPacket(endPhoneFileTransferPacket, kDefaultTimeoutSend, kEmptyTransferBeforeAndAfter); delete endPhoneFileTransferPacket; if (!success) { Interface::PrintErrorSameLine("\n"); Interface::PrintError("Failed to end phone file transfer sequence!\n"); return (false); } } else // destination == EndFileTransferPacket::kDestinationModem { EndModemFileTransferPacket *endModemFileTransferPacket = new EndModemFileTransferPacket(sequenceEffectiveByteCount, 0, deviceType, isLastSequence); success = SendPacket(endModemFileTransferPacket, kDefaultTimeoutSend, kEmptyTransferBeforeAndAfter); delete endModemFileTransferPacket; if (!success) { Interface::PrintErrorSameLine("\n"); Interface::PrintError("Failed to end modem file transfer sequence!\n"); return (false); } } fileTransferResponse = new ResponsePacket(ResponsePacket::kResponseTypeFileTransfer); success = ReceivePacket(fileTransferResponse, fileTransferSequenceTimeout); delete fileTransferResponse; if (!success) { Interface::PrintErrorSameLine("\n"); Interface::PrintError("Failed to confirm end of file transfer sequence!\n"); return (false); } } if (!verbose) Interface::Print("\n"); return (true); } void BridgeManager::SetUsbLogLevel(UsbLogLevel usbLogLevel) { this->usbLogLevel = usbLogLevel; if (libusbContext) { switch (usbLogLevel) { case UsbLogLevel::None: libusb_set_option(libusbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE); break; case UsbLogLevel::Error: libusb_set_option(libusbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_ERROR); break; case UsbLogLevel::Warning: libusb_set_option(libusbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_WARNING); break; case UsbLogLevel::Info: libusb_set_option(libusbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO); break; case UsbLogLevel::Debug: libusb_set_option(libusbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG); break; } } } #ifdef OS_LINUX bool BridgeManager::IsUbuntu() { std::ifstream os_release("/etc/os-release"); std::string line, entry, os; int pos; while (std::getline(os_release, line)) { pos = line.find("="); entry = line.substr(0, pos); if (entry == "ID") { os = line.substr(pos+1); if (verbose) { Interface::Print("Linux distro ID: %s\n", os.c_str()); } if (os == "ubuntu") { return true; } break; } } return false; } #endif Heimdall-v2.1.0/heimdall/source/BridgeManager.h000066400000000000000000000105031464356164000213460ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef BRIDGEMANAGER_H #define BRIDGEMANAGER_H // libpit #include "libpit.h" // Heimdall #include "Heimdall.h" struct libusb_context; struct libusb_device; struct libusb_device_handle; namespace Heimdall { class InboundPacket; class OutboundPacket; class DeviceIdentifier { public: const int vendorId; const int productId; DeviceIdentifier(int vid, int pid) : vendorId(vid), productId(pid) { } }; class BridgeManager { public: enum { kSupportedDeviceCount = 3 }; enum { kInitialiseSucceeded = 0, kInitialiseFailed, kInitialiseDeviceNotDetected }; enum { kVidSamsung = 0x04E8 }; enum { kPidGalaxyS = 0x6601, kPidGalaxyS2 = 0x685D, kPidDroidCharge = 0x68C3 }; enum { kDefaultTimeoutSend = 3000, kDefaultTimeoutReceive = 3000, kDefaultTimeoutEmptyTransfer = 100 }; enum class UsbLogLevel { None = 0, Error, Warning, Info, Debug, Default = Error }; enum { kEmptyTransferNone = 0, kEmptyTransferBefore = 1, kEmptyTransferAfter = 1 << 1, kEmptyTransferBeforeAndAfter = kEmptyTransferBefore | kEmptyTransferAfter }; private: static const DeviceIdentifier supportedDevices[kSupportedDeviceCount]; bool verbose; bool waitForDevice; libusb_context *libusbContext; libusb_device_handle *deviceHandle; libusb_device *heimdallDevice; int interfaceIndex; int altSettingIndex; int inEndpoint; int outEndpoint; bool interfaceClaimed; #ifdef OS_LINUX bool detachedDriver; #endif unsigned int fileTransferSequenceMaxLength; unsigned int fileTransferPacketSize; unsigned int fileTransferSequenceTimeout; UsbLogLevel usbLogLevel; int FindDeviceInterface(void); bool ClaimDeviceInterface(void); bool SetupDeviceInterface(void); void ReleaseDeviceInterface(void); bool InitialiseProtocol(void); bool SendBulkTransfer(unsigned char *data, int length, int timeout, bool retry = true) const; int ReceiveBulkTransfer(unsigned char *data, int length, int timeout, bool retry = true) const; public: BridgeManager(bool verbose, bool waitForDevice); ~BridgeManager(); bool DetectDevice(void); int Initialise(bool resume); bool BeginSession(void); bool EndSession(bool reboot) const; bool SendPacket(OutboundPacket *packet, int timeout = kDefaultTimeoutSend, int emptyTransferFlags = kEmptyTransferAfter) const; bool ReceivePacket(InboundPacket *packet, int timeout = kDefaultTimeoutReceive, int emptyTransferFlags = kEmptyTransferNone) const; bool RequestDeviceType(unsigned int request, int *result) const; bool SendPitData(const libpit::PitData *pitData) const; int ReceivePitFile(unsigned char **pitBuffer) const; int DownloadPitFile(unsigned char **pitBuffer) const; // Thin wrapper around ReceivePitFile() with additional logging. bool SendFile(FILE *file, unsigned int destination, unsigned int deviceType, unsigned int fileIdentifier = 0xFFFFFFFF) const; void SetUsbLogLevel(UsbLogLevel usbLogLevel); #ifdef OS_LINUX bool IsUbuntu(void); #endif UsbLogLevel GetUsbLogLevel(void) const { return usbLogLevel; } bool IsVerbose(void) const { return (verbose); } }; } #endif Heimdall-v2.1.0/heimdall/source/ClosePcScreenAction.cpp000066400000000000000000000105601464356164000230430ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Heimdall #include "Arguments.h" #include "BridgeManager.h" #include "ClosePcScreenAction.h" #include "Heimdall.h" #include "Interface.h" using namespace std; using namespace Heimdall; const char *ClosePcScreenAction::usage = "Action: close-pc-screen\n\ Arguments: [--verbose] [--no-reboot] [--resume] [--stdout-errors]\n\ [--usb-log-level ]\n\ Description: Attempts to get rid off the \"connect phone to PC\" screen.\n\ Note: --no-reboot causes the device to remain in download mode after the action\n\ is completed. If you wish to perform another action whilst remaining in\n\ download mode, then the following action must specify the --resume flag.\n"; int ClosePcScreenAction::Execute(int argc, char **argv) { // Handle arguments map argumentTypes; argumentTypes["no-reboot"] = kArgumentTypeFlag; argumentTypes["resume"] = kArgumentTypeFlag; argumentTypes["verbose"] = kArgumentTypeFlag; argumentTypes["stdout-errors"] = kArgumentTypeFlag; argumentTypes["usb-log-level"] = kArgumentTypeString; Arguments arguments(argumentTypes); if (!arguments.ParseArguments(argc, argv, 2)) { Interface::Print(ClosePcScreenAction::usage); return (0); } const StringArgument *usbLogLevelArgument = static_cast(arguments.GetArgument("usb-log-level")); BridgeManager::UsbLogLevel usbLogLevel = BridgeManager::UsbLogLevel::Default; if (usbLogLevelArgument) { const string& usbLogLevelString = usbLogLevelArgument->GetValue(); if (usbLogLevelString.compare("none") == 0 || usbLogLevelString.compare("NONE") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::None; } else if (usbLogLevelString.compare("error") == 0 || usbLogLevelString.compare("ERROR") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Error; } else if (usbLogLevelString.compare("warning") == 0 || usbLogLevelString.compare("WARNING") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Warning; } else if (usbLogLevelString.compare("info") == 0 || usbLogLevelString.compare("INFO") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Info; } else if (usbLogLevelString.compare("debug") == 0 || usbLogLevelString.compare("DEBUG") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Debug; } else { Interface::Print("Unknown USB log level: %s\n\n", usbLogLevelString.c_str()); Interface::Print(ClosePcScreenAction::usage); return (0); } } bool reboot = arguments.GetArgument("no-reboot") == nullptr; bool resume = arguments.GetArgument("resume") != nullptr; bool verbose = arguments.GetArgument("verbose") != nullptr; if (arguments.GetArgument("stdout-errors") != nullptr) Interface::SetStdoutErrors(true); // Info Interface::PrintReleaseInfo(); Sleep(1000); // Download PIT file from device. BridgeManager *bridgeManager = new BridgeManager(verbose, false); bridgeManager->SetUsbLogLevel(usbLogLevel); if (bridgeManager->Initialise(resume) != BridgeManager::kInitialiseSucceeded || !bridgeManager->BeginSession()) { delete bridgeManager; return (1); } Interface::Print("Attempting to close connect to pc screen...\n"); bool success = bridgeManager->EndSession(reboot); delete bridgeManager; if (success) { Interface::Print("Attempt complete\n"); return (0); } else { return (1); } } Heimdall-v2.1.0/heimdall/source/ClosePcScreenAction.h000066400000000000000000000024231464356164000225070ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef CLOSEPCSCREENACTION_H #define CLOSEPCSCREENACTION_H namespace Heimdall { namespace ClosePcScreenAction { extern const char *usage; int Execute(int argc, char **argv); } } #endif Heimdall-v2.1.0/heimdall/source/ControlPacket.h000066400000000000000000000034231464356164000214320ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef CONTROLPACKET_H #define CONTROLPACKET_H // Heimdall #include "OutboundPacket.h" namespace Heimdall { class ControlPacket : public OutboundPacket { public: enum { kControlTypeSession = 0x64, kControlTypePitFile = 0x65, kControlTypeFileTransfer = 0x66, kControlTypeEndSession = 0x67 }; protected: enum { kDataSize = 4 }; private: unsigned int controlType; public: ControlPacket(unsigned int controlType) : OutboundPacket(1024) { this->controlType = controlType; } unsigned int GetControlType(void) const { return (controlType); } virtual void Pack(void) { PackInteger(0, controlType); } }; } #endif Heimdall-v2.1.0/heimdall/source/DetectAction.cpp000066400000000000000000000073661464356164000215750ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Heimdall #include "Arguments.h" #include "BridgeManager.h" #include "DetectAction.h" #include "Heimdall.h" #include "Interface.h" using namespace std; using namespace Heimdall; const char *DetectAction::usage = "Action: detect\n\ Arguments: [--wait] [--verbose] [--stdout-errors]\n\ [--usb-log-level ]\n\ Description: Indicates whether or not a download mode device can be detected.\n\ Returns instantly per default, or waits until device is found\n\ when --wait argument is used.\n"; int DetectAction::Execute(int argc, char **argv) { // Handle arguments map argumentTypes; argumentTypes["verbose"] = kArgumentTypeFlag; argumentTypes["wait"] = kArgumentTypeFlag; argumentTypes["stdout-errors"] = kArgumentTypeFlag; argumentTypes["usb-log-level"] = kArgumentTypeString; Arguments arguments(argumentTypes); if (!arguments.ParseArguments(argc, argv, 2)) { Interface::Print(DetectAction::usage); return (0); } bool waitForDevice = arguments.GetArgument("wait") != nullptr; bool verbose = arguments.GetArgument("verbose") != nullptr; if (arguments.GetArgument("stdout-errors") != nullptr) Interface::SetStdoutErrors(true); const StringArgument *usbLogLevelArgument = static_cast(arguments.GetArgument("usb-log-level")); BridgeManager::UsbLogLevel usbLogLevel = BridgeManager::UsbLogLevel::Default; if (usbLogLevelArgument) { const string& usbLogLevelString = usbLogLevelArgument->GetValue(); if (usbLogLevelString.compare("none") == 0 || usbLogLevelString.compare("NONE") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::None; } else if (usbLogLevelString.compare("error") == 0 || usbLogLevelString.compare("ERROR") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Error; } else if (usbLogLevelString.compare("warning") == 0 || usbLogLevelString.compare("WARNING") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Warning; } else if (usbLogLevelString.compare("info") == 0 || usbLogLevelString.compare("INFO") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Info; } else if (usbLogLevelString.compare("debug") == 0 || usbLogLevelString.compare("DEBUG") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Debug; } else { Interface::Print("Unknown USB log level: %s\n\n", usbLogLevelString.c_str()); Interface::Print(DetectAction::usage); return (0); } } // Download PIT file from device. BridgeManager *bridgeManager = new BridgeManager(verbose, waitForDevice); bridgeManager->SetUsbLogLevel(usbLogLevel); bool detected = bridgeManager->DetectDevice(); delete bridgeManager; return ((detected) ? 0 : 1); } Heimdall-v2.1.0/heimdall/source/DetectAction.h000066400000000000000000000023761464356164000212360ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef DETECTACTION_H #define DETECTACTION_H namespace Heimdall { namespace DetectAction { extern const char *usage; int Execute(int argc, char **argv); } } #endif Heimdall-v2.1.0/heimdall/source/DeviceTypePacket.h000066400000000000000000000025561464356164000220610ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef DEVICETYPEPACKET_H #define DEVICETYPEPACKET_H // Heimdall #include "SessionSetupPacket.h" namespace Heimdall { class DeviceTypePacket : public SessionSetupPacket { public: DeviceTypePacket() : SessionSetupPacket(SessionSetupPacket::kDeviceType) { } }; } #endif Heimdall-v2.1.0/heimdall/source/DownloadPitAction.cpp000066400000000000000000000127271464356164000226060ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // C Standard Library #include // Heimdall #include "Arguments.h" #include "BridgeManager.h" #include "DownloadPitAction.h" #include "Heimdall.h" #include "Interface.h" using namespace std; using namespace Heimdall; const char *DownloadPitAction::usage = "Action: download-pit\n\ Arguments: --output [--verbose] [--no-reboot] [--stdout-errors]\n\ [--wait] [--usb-log-level ]\n\ Description: Downloads the connected device's PIT file to the specified\n\ output file. If --wait is used Heimdall waits until a compatible device is\n\ connected.\n\ Note: --no-reboot causes the device to remain in download mode after the action\n\ is completed. If you wish to perform another action whilst remaining in\n\ download mode, then the following action must specify the --resume flag.\n"; int DownloadPitAction::Execute(int argc, char **argv) { // Handle arguments map argumentTypes; argumentTypes["output"] = kArgumentTypeString; argumentTypes["no-reboot"] = kArgumentTypeFlag; argumentTypes["resume"] = kArgumentTypeFlag; argumentTypes["verbose"] = kArgumentTypeFlag; argumentTypes["wait"] = kArgumentTypeFlag; argumentTypes["stdout-errors"] = kArgumentTypeFlag; argumentTypes["usb-log-level"] = kArgumentTypeString; Arguments arguments(argumentTypes); if (!arguments.ParseArguments(argc, argv, 2)) { Interface::Print(DownloadPitAction::usage); return (0); } const StringArgument *outputArgument = static_cast(arguments.GetArgument("output")); if (!outputArgument) { Interface::Print("Output file was not specified.\n\n"); Interface::Print(DownloadPitAction::usage); return (0); } bool waitForDevice = arguments.GetArgument("wait") == nullptr; bool reboot = arguments.GetArgument("no-reboot") == nullptr; bool resume = arguments.GetArgument("resume") != nullptr; bool verbose = arguments.GetArgument("verbose") != nullptr; if (arguments.GetArgument("stdout-errors") != nullptr) Interface::SetStdoutErrors(true); const StringArgument *usbLogLevelArgument = static_cast(arguments.GetArgument("usb-log-level")); BridgeManager::UsbLogLevel usbLogLevel = BridgeManager::UsbLogLevel::Default; if (usbLogLevelArgument) { const string& usbLogLevelString = usbLogLevelArgument->GetValue(); if (usbLogLevelString.compare("none") == 0 || usbLogLevelString.compare("NONE") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::None; } else if (usbLogLevelString.compare("error") == 0 || usbLogLevelString.compare("ERROR") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Error; } else if (usbLogLevelString.compare("warning") == 0 || usbLogLevelString.compare("WARNING") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Warning; } else if (usbLogLevelString.compare("info") == 0 || usbLogLevelString.compare("INFO") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Info; } else if (usbLogLevelString.compare("debug") == 0 || usbLogLevelString.compare("DEBUG") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Debug; } else { Interface::Print("Unknown USB log level: %s\n\n", usbLogLevelString.c_str()); Interface::Print(DownloadPitAction::usage); return (0); } } // Info Interface::PrintReleaseInfo(); Sleep(1000); // Open output file const char *outputFilename = outputArgument->GetValue().c_str(); FILE *outputPitFile = FileOpen(outputFilename, "wb"); if (!outputPitFile) { Interface::PrintError("Failed to open output file \"%s\"\n", outputFilename); return (1); } // Download PIT file from device. BridgeManager *bridgeManager = new BridgeManager(verbose, waitForDevice); bridgeManager->SetUsbLogLevel(usbLogLevel); if (bridgeManager->Initialise(resume) != BridgeManager::kInitialiseSucceeded || !bridgeManager->BeginSession()) { FileClose(outputPitFile); delete bridgeManager; return (1); } unsigned char *pitBuffer; int fileSize = bridgeManager->DownloadPitFile(&pitBuffer); bool success = true; if (fileSize > 0) { if (fwrite(pitBuffer, 1, fileSize, outputPitFile) != static_cast(fileSize)) { Interface::PrintError("Failed to write PIT data to output file.\n"); success = false; } } else { success = false; } if (!bridgeManager->EndSession(reboot)) success = false; delete bridgeManager; FileClose(outputPitFile); delete [] pitBuffer; return (success ? 0 : 1); } Heimdall-v2.1.0/heimdall/source/DownloadPitAction.h000066400000000000000000000024151464356164000222440ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef DOWNLOADPITACTION_H #define DOWNLOADPITACTION_H namespace Heimdall { namespace DownloadPitAction { extern const char *usage; int Execute(int argc, char **argv); } } #endif Heimdall-v2.1.0/heimdall/source/DumpPartFileTransferPacket.h000066400000000000000000000033041464356164000240510ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef DUMPPARTFILETRANSFERPACKET_H #define DUMPPARTFILETRANSFERPACKET_H // Heimdall #include "FileTransferPacket.h" namespace Heimdall { class DumpPartFileTransferPacket : public FileTransferPacket { private: unsigned int partIndex; public: DumpPartFileTransferPacket(unsigned int partIndex) : FileTransferPacket(FileTransferPacket::kRequestPart) { this->partIndex = partIndex; } unsigned int GetPartIndex(void) const { return (partIndex); } virtual void Pack(void) { FileTransferPacket::Pack(); PackInteger(FileTransferPacket::kDataSize, partIndex); } }; } #endif Heimdall-v2.1.0/heimdall/source/DumpPartPitFilePacket.h000066400000000000000000000032121464356164000230170ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef DUMPPARTPITFILEPACKET_H #define DUMPPARTPITFILEPACKET_H // Heimdall #include "PitFilePacket.h" namespace Heimdall { class DumpPartPitFilePacket : public PitFilePacket { private: unsigned int partIndex; public: DumpPartPitFilePacket(unsigned int partIndex) : PitFilePacket(PitFilePacket::kRequestPart) { this->partIndex = partIndex; } unsigned int GetPartIndex(void) const { return (partIndex); } void Pack(void) { PitFilePacket::Pack(); PackInteger(PitFilePacket::kDataSize, partIndex); } }; } #endif Heimdall-v2.1.0/heimdall/source/DumpResponse.h000066400000000000000000000031471464356164000213110ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef DUMPRESPONSE_H #define DUMPRESPONSE_H // Heimdall #include "ResponsePacket.h" namespace Heimdall { class DumpResponse : public ResponsePacket { private: unsigned int dumpSize; public: DumpResponse() : ResponsePacket(kResponseTypeFileTransfer) { } unsigned int GetDumpSize(void) const { return (dumpSize); } bool Unpack(void) { if (!ResponsePacket::Unpack()) return (false); dumpSize = UnpackInteger(ResponsePacket::kDataSize); return (true); } }; } #endif Heimdall-v2.1.0/heimdall/source/EnableTFlashPacket.h000066400000000000000000000025701464356164000223040ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef ENABLETFLASHPACKET_H #define ENABLETFLASHPACKET_H // Heimdall #include "SessionSetupPacket.h" namespace Heimdall { class EnableTFlashPacket : public SessionSetupPacket { public: EnableTFlashPacket() : SessionSetupPacket(SessionSetupPacket::kEnableTFlash) { } }; } #endif Heimdall-v2.1.0/heimdall/source/EndFileTransferPacket.h000066400000000000000000000051551464356164000230310ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef ENDFILETRANSFERPACKET_H #define ENDFILETRANSFERPACKET_H // Heimdall #include "FileTransferPacket.h" namespace Heimdall { class EndFileTransferPacket : public FileTransferPacket { public: enum { kDestinationPhone = 0x00, kDestinationModem = 0x01 }; protected: enum { kDataSize = FileTransferPacket::kDataSize + 16 }; private: unsigned int destination; // PDA / Modem unsigned int sequenceByteCount; unsigned int unknown1; // EFS? unsigned int deviceType; protected: EndFileTransferPacket(unsigned int destination, unsigned int sequenceByteCount, unsigned int unknown1, unsigned int deviceType) : FileTransferPacket(FileTransferPacket::kRequestEnd) { this->destination = destination; this->sequenceByteCount = sequenceByteCount; this->unknown1 = unknown1; this->deviceType = deviceType; } public: unsigned int GetDestination(void) const { return (destination); } unsigned int GetSequenceByteCount(void) const { return (sequenceByteCount); } unsigned int GetUnknown1(void) const { return (unknown1); } unsigned int GetDeviceType(void) const { return (deviceType); } virtual void Pack(void) { FileTransferPacket::Pack(); PackInteger(FileTransferPacket::kDataSize, destination); PackInteger(FileTransferPacket::kDataSize + 4, sequenceByteCount); PackInteger(FileTransferPacket::kDataSize + 8, unknown1); PackInteger(FileTransferPacket::kDataSize + 12, deviceType); } }; } #endif Heimdall-v2.1.0/heimdall/source/EndModemFileTransferPacket.h000066400000000000000000000035261464356164000240130ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef ENDMODEMFILETRANSFERPACKET_H #define ENDMODEMFILETRANSFERPACKET_H // Heimdall #include "EndFileTransferPacket.h" namespace Heimdall { class EndModemFileTransferPacket : public EndFileTransferPacket { private: unsigned int endOfFile; public: EndModemFileTransferPacket(unsigned int sequenceByteCount, unsigned int unknown1, unsigned int chipIdentifier, bool endOfFile) : EndFileTransferPacket(EndFileTransferPacket::kDestinationModem, sequenceByteCount, unknown1, chipIdentifier) { this->endOfFile = (endOfFile) ? 1 : 0; } bool IsEndOfFile(void) const { return (endOfFile == 1); } void Pack(void) { EndFileTransferPacket::Pack(); PackInteger(EndFileTransferPacket::kDataSize, endOfFile); } }; } #endif Heimdall-v2.1.0/heimdall/source/EndPhoneFileTransferPacket.h000066400000000000000000000053251464356164000240220ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef ENDPHONEFILETRANSFERPACKET_H #define ENDPHONEFILETRANSFERPACKET_H // Heimdall #include "EndFileTransferPacket.h" namespace Heimdall { class EndPhoneFileTransferPacket : public EndFileTransferPacket { public: /*enum { kFilePrimaryBootloader = 0x00, kFilePit = 0x01, // New 1.1 - Don't flash the pit this way! kFileSecondaryBootloader = 0x03, kFileSecondaryBootloaderBackup = 0x04, // New 1.1 kFileKernel = 0x06, kFileRecovery = 0x07, // New 1.1 kFileTabletModem = 0x08, // New 1.2 kFileEfs = 0x14, // New 1.1 kFileParamLfs = 0x15, kFileFactoryFilesystem = 0x16, kFileDatabaseData = 0x17, kFileCache = 0x18, kFileModem = 0x0B // New 1.1 - Kies flashes the modem this way rather than using the EndModemFileTransferPacket. };*/ private: unsigned int fileIdentifier; unsigned int endOfFile; public: EndPhoneFileTransferPacket(unsigned int sequenceByteCount, unsigned int unknown1, unsigned int chipIdentifier, unsigned int fileIdentifier, bool endOfFile) : EndFileTransferPacket(EndFileTransferPacket::kDestinationPhone, sequenceByteCount, unknown1, chipIdentifier) { this->fileIdentifier = fileIdentifier; this->endOfFile = (endOfFile) ? 1 : 0; } unsigned int GetFileIdentifier(void) { return (fileIdentifier); } bool IsEndOfFile(void) const { return (endOfFile == 1); } void Pack(void) { EndFileTransferPacket::Pack(); PackInteger(EndFileTransferPacket::kDataSize, fileIdentifier); PackInteger(EndFileTransferPacket::kDataSize + 4, endOfFile); } }; } #endif Heimdall-v2.1.0/heimdall/source/EndPitFileTransferPacket.h000066400000000000000000000032261464356164000235030ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef ENDPITFILETRANSFERPACKET_H #define ENDPITFILETRANSFERPACKET_H // Heimdall #include "PitFilePacket.h" namespace Heimdall { class EndPitFileTransferPacket : public PitFilePacket { private: unsigned int fileSize; public: EndPitFileTransferPacket(unsigned int fileSize) : PitFilePacket(PitFilePacket::kRequestEndTransfer) { this->fileSize = fileSize; } unsigned int GetFileSize(void) const { return (fileSize); } void Pack(void) { PitFilePacket::Pack(); PackInteger(PitFilePacket::kDataSize, fileSize); } }; } #endif Heimdall-v2.1.0/heimdall/source/EndSessionPacket.h000066400000000000000000000033121464356164000220610ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef ENDSESSIONPACKET_H #define ENDSESSIONPACKET_H // Heimdall #include "ControlPacket.h" namespace Heimdall { class EndSessionPacket : public ControlPacket { public: enum { kRequestEndSession = 0, kRequestRebootDevice = 1 }; private: unsigned int request; public: EndSessionPacket(unsigned int request) : ControlPacket(ControlPacket::kControlTypeEndSession) { this->request = request; } unsigned int GetRequest(void) const { return (request); } void Pack(void) { ControlPacket::Pack(); PackInteger(ControlPacket::kDataSize, request); } }; } #endif Heimdall-v2.1.0/heimdall/source/FilePartSizePacket.h000066400000000000000000000032601464356164000223520ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef FILEPARTSIZEPACKET_H #define FILEPARTSIZEPACKET_H // Heimdall #include "SessionSetupPacket.h" namespace Heimdall { class FilePartSizePacket : public SessionSetupPacket { private: unsigned int filePartSize; public: FilePartSizePacket(unsigned int filePartSize) : SessionSetupPacket(SessionSetupPacket::kFilePartSize) { this->filePartSize = filePartSize; } unsigned int GetFilePartSize(void) const { return filePartSize; } void Pack(void) { SessionSetupPacket::Pack(); PackInteger(SessionSetupPacket::kDataSize, filePartSize); } }; } #endif Heimdall-v2.1.0/heimdall/source/FileTransferPacket.h000066400000000000000000000035321464356164000223770ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef FILETRANSFERPACKET_H #define FILETRANSFERPACKET_H // Heimdall #include "ControlPacket.h" namespace Heimdall { class FileTransferPacket : public ControlPacket { public: enum { kRequestFlash = 0x00, kRequestDump = 0x01, kRequestPart = 0x02, kRequestEnd = 0x03 }; protected: enum { kDataSize = ControlPacket::kDataSize + 4 }; private: unsigned int request; public: FileTransferPacket(unsigned int request) : ControlPacket(ControlPacket::kControlTypeFileTransfer) { this->request = request; } unsigned int GetRequest(void) const { return (request); } virtual void Pack(void) { ControlPacket::Pack(); PackInteger(ControlPacket::kDataSize, request); } }; } #endif Heimdall-v2.1.0/heimdall/source/FlashAction.cpp000066400000000000000000000445231464356164000214160ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // C Standard Library #include // Heimdall #include "Arguments.h" #include "BridgeManager.h" #include "EnableTFlashPacket.h" #include "EndModemFileTransferPacket.h" #include "EndPhoneFileTransferPacket.h" #include "FlashAction.h" #include "Heimdall.h" #include "Interface.h" #include "SessionSetupResponse.h" #include "TotalBytesPacket.h" #include "Utility.h" using namespace std; using namespace libpit; using namespace Heimdall; const char *FlashAction::usage = "Action: flash\n\ Arguments:\n\ [-- ...]\n\ [-- ...]\n\ [--pit ] [--verbose] [--no-reboot] [--resume] [--stdout-errors]\n\ [--usb-log-level ] [--skip-size-check] [--wait]\n\ or:\n\ --repartition --pit [-- ...]\n\ [-- ...] [--verbose] [--no-reboot]\n\ [--resume] [--stdout-errors] [--usb-log-level ]\n\ [--tflash] [--skip-size-check] [--wait]\n\ Description: Flashes one or more firmware files to your phone. Partition names\n\ (or identifiers) can be obtained by executing the print-pit action.\n\ With --wait Heimdall waits until a compatible device is connected.\n\ T-Flash mode allows to flash the inserted SD-card instead of the internal MMC.\n\ Use --skip-size-check to not verify that files fit in the specified partition.\n\ Note: --no-reboot causes the device to remain in download mode after the action\n\ is completed. If you wish to perform another action whilst remaining in\n\ download mode, then the following action must specify the --resume flag.\n\ WARNING: If you're repartitioning it's strongly recommended you specify\n\ all files at your disposal.\n"; struct PartitionFile { const char *argumentName; FILE *file; unsigned long fileSize; PartitionFile(const char *argumentName, FILE *file, unsigned long fileSize) { this->argumentName = argumentName; this->file = file; this->fileSize = fileSize; } }; struct PartitionFlashInfo { const PitEntry *pitEntry; FILE *file; PartitionFlashInfo(const PitEntry *pitEntry, FILE *file) { this->pitEntry = pitEntry; this->file = file; } }; static bool openFiles(Arguments& arguments, vector& partitionFiles, FILE *& pitFile) { // Open PIT file const StringArgument *pitArgument = static_cast(arguments.GetArgument("pit")); if (pitArgument) { pitFile = FileOpen(pitArgument->GetValue().c_str(), "rb"); if (!pitFile) { Interface::PrintError("Failed to open file \"%s\"\n", pitArgument->GetValue().c_str()); return (false); } } // Open partition files for (vector::const_iterator it = arguments.GetArguments().begin(); it != arguments.GetArguments().end(); it++) { const string& argumentName = (*it)->GetName(); // The only way an argument could exist without being in the argument types map is if it's a wild-card. // The "%d" wild-card refers to a partition by identifier, where as the "%s" wild-card refers to a // partition by name. if (arguments.GetArgumentTypes().find(argumentName) == arguments.GetArgumentTypes().end()) { const StringArgument *stringArgument = static_cast(*it); FILE *file = FileOpen(stringArgument->GetValue().c_str(), "rb"); if (!file) { Interface::PrintError("Failed to open file \"%s\"\n", stringArgument->GetValue().c_str()); return (false); } FileSeek(file, 0, SEEK_END); unsigned long fileSize = (unsigned long)FileTell(file); FileRewind(file); partitionFiles.push_back(PartitionFile(argumentName.c_str(), file, fileSize)); } } return (true); } static void closeFiles(vector& partitionFiles, FILE *& pitFile) { // Close PIT file if (pitFile) { FileClose(pitFile); pitFile = nullptr; } // Close partition files for (vector::const_iterator it = partitionFiles.begin(); it != partitionFiles.end(); it++) FileClose(it->file); partitionFiles.clear(); } static bool sendTotalTransferSize(BridgeManager *bridgeManager, const vector& partitionFiles, FILE *pitFile, bool repartition) { unsigned long totalBytes = 0; for (vector::const_iterator it = partitionFiles.begin(); it != partitionFiles.end(); it++) { totalBytes += it->fileSize; } if (repartition) { FileSeek(pitFile, 0, SEEK_END); totalBytes += (unsigned long)FileTell(pitFile); FileRewind(pitFile); } bool success; TotalBytesPacket *totalBytesPacket = new TotalBytesPacket(totalBytes); success = bridgeManager->SendPacket(totalBytesPacket); delete totalBytesPacket; if (!success) { Interface::PrintError("Failed to send total bytes packet!\n"); return (false); } SessionSetupResponse *totalBytesResponse = new SessionSetupResponse(); success = bridgeManager->ReceivePacket(totalBytesResponse); int totalBytesResult = totalBytesResponse->GetResult(); delete totalBytesResponse; if (!success) { Interface::PrintError("Failed to receive session total bytes response!\n"); return (false); } if (totalBytesResult != 0) { Interface::PrintError("Unexpected session total bytes response!\nExpected: 0\nReceived:%d\n", totalBytesResult); return (false); } return (true); } static bool setupPartitionFlashInfo(const vector& partitionFiles, const PitData *pitData, vector& partitionFlashInfos) { for (vector::const_iterator it = partitionFiles.begin(); it != partitionFiles.end(); it++) { const PitEntry *pitEntry = nullptr; // Was the argument a partition identifier? unsigned int partitionIdentifier; if (Utility::ParseUnsignedInt(partitionIdentifier, it->argumentName) == kNumberParsingStatusSuccess) { pitEntry = pitData->FindEntry(partitionIdentifier); if (!pitEntry) { Interface::PrintError("No partition with identifier \"%s\" exists in the specified PIT.\n", it->argumentName); return (false); } } else { // The argument must be an partition name e.g. "ZIMAGE" pitEntry = pitData->FindEntry(it->argumentName); if (!pitEntry) { Interface::PrintError("Partition \"%s\" does not exist in the specified PIT.\n", it->argumentName); return (false); } } partitionFlashInfos.push_back(PartitionFlashInfo(pitEntry, it->file)); } return (true); } static bool flashPitData(BridgeManager *bridgeManager, const PitData *pitData) { Interface::Print("Uploading PIT\n"); if (bridgeManager->SendPitData(pitData)) { Interface::Print("PIT upload successful\n\n"); return (true); } else { Interface::PrintError("PIT upload failed!\n\n"); return (false); } } static bool flashFile(BridgeManager *bridgeManager, const PartitionFlashInfo& partitionFlashInfo) { if (partitionFlashInfo.pitEntry->GetBinaryType() == PitEntry::kBinaryTypeCommunicationProcessor) // Modem { Interface::Print("Uploading %s\n", partitionFlashInfo.pitEntry->GetPartitionName()); if (bridgeManager->SendFile(partitionFlashInfo.file, EndModemFileTransferPacket::kDestinationModem, partitionFlashInfo.pitEntry->GetDeviceType())) { Interface::Print("%s upload successful\n\n", partitionFlashInfo.pitEntry->GetPartitionName()); return (true); } else { Interface::PrintError("%s upload failed!\n\n", partitionFlashInfo.pitEntry->GetPartitionName()); return (false); } } else // partitionFlashInfo.pitEntry->GetBinaryType() == PitEntry::kBinaryTypeApplicationProcessor { Interface::Print("Uploading %s\n", partitionFlashInfo.pitEntry->GetPartitionName()); if (bridgeManager->SendFile(partitionFlashInfo.file, EndPhoneFileTransferPacket::kDestinationPhone, partitionFlashInfo.pitEntry->GetDeviceType(), partitionFlashInfo.pitEntry->GetIdentifier())) { Interface::Print("%s upload successful\n\n", partitionFlashInfo.pitEntry->GetPartitionName()); return (true); } else { Interface::PrintError("%s upload failed!\n\n", partitionFlashInfo.pitEntry->GetPartitionName()); return (false); } } } static bool flashPartitions(BridgeManager *bridgeManager, const vector& partitionFiles, const PitData *pitData, bool repartition, bool skipSizeCheck) { vector partitionFlashInfos; // Map the files being flashed to partitions stored in the PIT file. if (!setupPartitionFlashInfo(partitionFiles, pitData, partitionFlashInfos)) return (false); /* Verify that the files we want to flash fit in partitions */ if (!skipSizeCheck) { for (vector::const_iterator it = partitionFiles.begin(); it != partitionFiles.end(); it++) { unsigned int partitionIdentifier; const PitEntry *part; if (Utility::ParseUnsignedInt(partitionIdentifier, it->argumentName) == kNumberParsingStatusSuccess) { part = pitData->FindEntry(partitionIdentifier); if (!part) { Interface::PrintError("No partition with identifier \"%s\" exists in the specified PIT.\n", it->argumentName); return (false); } } else { part = pitData->FindEntry(it->argumentName); if (!part) { Interface::PrintError("Partition \"%s\" does not exist in the specified PIT.\n", it->argumentName); return (false); } } if (part->GetDeviceType() != PitEntry::kDeviceTypeMMC && part->GetDeviceType() != PitEntry::kDeviceTypeUFS) continue; unsigned long partitionSize = part->GetBlockCount(); unsigned int blockSize = 512; if (part->GetDeviceType() == PitEntry::kDeviceTypeUFS) blockSize = 4096; if (partitionSize > 0 && it->fileSize > partitionSize*blockSize) { Interface::PrintError("%s partition is too small for given file. Use --skip-size-check to flash anyways.\n", it->argumentName); return (false); } } } // If we're repartitioning then we need to flash the PIT file first (if it is listed in the PIT file). if (repartition) { if (!flashPitData(bridgeManager, pitData)) return (false); } // Flash partitions in the same order that arguments were specified in. for (vector::const_iterator it = partitionFlashInfos.begin(); it != partitionFlashInfos.end(); it++) { if (!flashFile(bridgeManager, *it)) return (false); } return (true); } static PitData *getPitData(BridgeManager *bridgeManager, FILE *pitFile, bool repartition) { PitData *pitData; PitData *localPitData = nullptr; // If a PIT file was passed as an argument then we must unpack it. if (pitFile) { // Load the local pit file into memory. FileSeek(pitFile, 0, SEEK_END); unsigned long localPitFileSize = (unsigned long)FileTell(pitFile); FileRewind(pitFile); unsigned char *pitFileBuffer = new unsigned char[localPitFileSize]; memset(pitFileBuffer, 0, localPitFileSize); int dataRead = fread(pitFileBuffer, 1, localPitFileSize, pitFile); if (dataRead > 0) { FileRewind(pitFile); localPitData = new PitData(); localPitData->Unpack(pitFileBuffer); delete [] pitFileBuffer; } else { Interface::PrintError("Failed to read PIT file.\n"); delete [] pitFileBuffer; return (nullptr); } } if (repartition) { // Use the local PIT file data. pitData = localPitData; } else { // If we're not repartitioning then we need to retrieve the device's PIT file and unpack it. unsigned char *pitFileBuffer; if (bridgeManager->DownloadPitFile(&pitFileBuffer) == 0) return (nullptr); pitData = new PitData(); pitData->Unpack(pitFileBuffer); delete [] pitFileBuffer; if (localPitData != nullptr) { // The user has specified a PIT without repartitioning, we should verify the local and device PIT data match! bool pitsMatch = pitData->Matches(localPitData); delete localPitData; if (!pitsMatch) { Interface::Print("Local and device PIT files don't match and repartition wasn't specified!\n"); Interface::PrintError("Flash aborted!\n"); return (nullptr); } } } return (pitData); } static bool enableTFlash(BridgeManager *bridgeManager) { bool success; EnableTFlashPacket *enableTFlashPacket = new EnableTFlashPacket(); success = bridgeManager->SendPacket(enableTFlashPacket); delete enableTFlashPacket; if (!success) { Interface::PrintError("Failed to send T-Flash packet!\n"); return false; } SessionSetupResponse *enableTFlashResponse = new SessionSetupResponse(); success = bridgeManager->ReceivePacket(enableTFlashResponse, 5000); unsigned int result = enableTFlashResponse->GetResult(); delete enableTFlashResponse; if (!success) { Interface::PrintError("Failed to receive T-Flash response!\n"); return false; } if (result) { Interface::PrintError("Unexpected T-Flash response!\nExpected: 0\nReceived: %d\n", result); return false; } return true; } int FlashAction::Execute(int argc, char **argv) { // Setup argument types map argumentTypes; map shortArgumentAliases; argumentTypes["repartition"] = kArgumentTypeFlag; argumentTypes["no-reboot"] = kArgumentTypeFlag; argumentTypes["resume"] = kArgumentTypeFlag; argumentTypes["verbose"] = kArgumentTypeFlag; argumentTypes["stdout-errors"] = kArgumentTypeFlag; argumentTypes["usb-log-level"] = kArgumentTypeString; argumentTypes["wait"] = kArgumentTypeFlag; argumentTypes["tflash"] = kArgumentTypeFlag; argumentTypes["skip-size-check"] = kArgumentTypeFlag; argumentTypes["pit"] = kArgumentTypeString; shortArgumentAliases["pit"] = "pit"; // Add wild-cards "%d" and "%s", for partition identifiers and partition names respectively. argumentTypes["%d"] = kArgumentTypeString; shortArgumentAliases["%d"] = "%d"; argumentTypes["%s"] = kArgumentTypeString; shortArgumentAliases["%s"] = "%s"; map argumentAliases; argumentAliases["PIT"] = "pit"; // Map upper-case PIT argument (i.e. partition name) to known lower-case pit argument. // Handle arguments Arguments arguments(argumentTypes, shortArgumentAliases, argumentAliases); if (!arguments.ParseArguments(argc, argv, 2)) { Interface::Print(FlashAction::usage); return (0); } bool reboot = arguments.GetArgument("no-reboot") == nullptr; bool resume = arguments.GetArgument("resume") != nullptr; bool verbose = arguments.GetArgument("verbose") != nullptr; bool tflash = arguments.GetArgument("tflash") != nullptr; bool waitForDevice = arguments.GetArgument("wait") != nullptr; // If we are flashing to sdcard we can ignore size of partition in PIT bool skipSizeCheck = (arguments.GetArgument("skip-size-check") != nullptr) || tflash; if (arguments.GetArgument("stdout-errors") != nullptr) Interface::SetStdoutErrors(true); const StringArgument *usbLogLevelArgument = static_cast(arguments.GetArgument("usb-log-level")); BridgeManager::UsbLogLevel usbLogLevel = BridgeManager::UsbLogLevel::Default; if (usbLogLevelArgument) { const string& usbLogLevelString = usbLogLevelArgument->GetValue(); if (usbLogLevelString.compare("none") == 0 || usbLogLevelString.compare("NONE") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::None; } else if (usbLogLevelString.compare("error") == 0 || usbLogLevelString.compare("ERROR") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Error; } else if (usbLogLevelString.compare("warning") == 0 || usbLogLevelString.compare("WARNING") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Warning; } else if (usbLogLevelString.compare("info") == 0 || usbLogLevelString.compare("INFO") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Info; } else if (usbLogLevelString.compare("debug") == 0 || usbLogLevelString.compare("DEBUG") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Debug; } else { Interface::Print("Unknown USB log level: %s\n\n", usbLogLevelString.c_str()); Interface::Print(FlashAction::usage); return (0); } } const StringArgument *pitArgument = static_cast(arguments.GetArgument("pit")); bool repartition = arguments.GetArgument("repartition") != nullptr; if (repartition && !pitArgument) { Interface::Print("If you wish to repartition then a PIT file must be specified.\n\n"); Interface::Print(FlashAction::usage); return (0); } // Open files FILE *pitFile = nullptr; vector partitionFiles; if (!openFiles(arguments, partitionFiles, pitFile)) { closeFiles(partitionFiles, pitFile); return (1); } if (partitionFiles.size() == 0) { Interface::Print(FlashAction::usage); return (0); } // Info Interface::PrintReleaseInfo(); Sleep(1000); // Perform flash BridgeManager *bridgeManager = new BridgeManager(verbose, waitForDevice); bridgeManager->SetUsbLogLevel(usbLogLevel); if (bridgeManager->Initialise(resume) != BridgeManager::kInitialiseSucceeded || !bridgeManager->BeginSession()) { closeFiles(partitionFiles, pitFile); delete bridgeManager; return (1); } if (tflash && !enableTFlash(bridgeManager)) { closeFiles(partitionFiles, pitFile); delete bridgeManager; return (1); } bool success = sendTotalTransferSize(bridgeManager, partitionFiles, pitFile, repartition); if (success) { PitData *pitData = getPitData(bridgeManager, pitFile, repartition); if (pitData) success = flashPartitions(bridgeManager, partitionFiles, pitData, repartition, skipSizeCheck); else success = false; delete pitData; } if (!bridgeManager->EndSession(reboot)) success = false; delete bridgeManager; closeFiles(partitionFiles, pitFile); return (success ? 0 : 1); } Heimdall-v2.1.0/heimdall/source/FlashAction.h000066400000000000000000000023731464356164000210600ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef FLASHACTION_H #define FLASHACTION_H namespace Heimdall { namespace FlashAction { extern const char *usage; int Execute(int argc, char **argv); } } #endif Heimdall-v2.1.0/heimdall/source/FlashPartFileTransferPacket.h000066400000000000000000000033741464356164000242100ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef FLASHPARTFILETRANSFERPACKET_H #define FLASHPARTFILETRANSFERPACKET_H // Heimdall #include "FileTransferPacket.h" namespace Heimdall { class FlashPartFileTransferPacket : public FileTransferPacket { private: unsigned int sequenceByteCount; public: FlashPartFileTransferPacket(unsigned int sequenceByteCount) : FileTransferPacket(FileTransferPacket::kRequestPart) { this->sequenceByteCount = sequenceByteCount; } unsigned int GetSequenceByteCount(void) const { return (sequenceByteCount); } void Pack(void) { FileTransferPacket::Pack(); PackInteger(FileTransferPacket::kDataSize, sequenceByteCount); } }; } #endif Heimdall-v2.1.0/heimdall/source/FlashPartPitFilePacket.h000066400000000000000000000032071464356164000231530ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef FLASHPARTPITFILEPACKET_H #define FLASHPARTPITFILEPACKET_H // Heimdall #include "PitFilePacket.h" namespace Heimdall { class FlashPartPitFilePacket : public PitFilePacket { private: unsigned int partSize; public: FlashPartPitFilePacket(unsigned int partSize) : PitFilePacket(PitFilePacket::kRequestPart) { this->partSize = partSize; } unsigned int GetPartSize(void) const { return (partSize); } void Pack(void) { PitFilePacket::Pack(); PackInteger(PitFilePacket::kDataSize, partSize); } }; } #endif Heimdall-v2.1.0/heimdall/source/Heimdall.h000066400000000000000000000037241464356164000204050ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef HEIMDALL_H #define HEIMDALL_H #ifdef _MSC_VER // Microsoft Visual C Standard Library #include #undef GetBinaryType #ifndef va_copy #define va_copy(d, s) ((d) = (s)) #endif #define FileOpen(FILE, MODE) fopen(FILE, MODE) #define FileClose(FILE) fclose(FILE) #define FileSeek(FILE, OFFSET, ORIGIN) _fseeki64(FILE, OFFSET, ORIGIN) #define FileTell(FILE) _ftelli64(FILE) #define FileRewind(FILE) rewind(FILE) #else // POSIX Standard Library #ifdef AUTOCONF #include "../config.h" #endif #include #define Sleep(t) usleep(1000*t) #define FileOpen(FILE, MODE) fopen(FILE, MODE) #define FileClose(FILE) fclose(FILE) #define FileSeek(FILE, OFFSET, ORIGIN) fseeko(FILE, OFFSET, ORIGIN) #define FileTell(FILE) ftello(FILE) #define FileRewind(FILE) rewind(FILE) #endif #if defined(_MSC_VER) && (_MSC_VER < 1700) # ifndef nullptr # define nullptr 0 # endif #endif #endif Heimdall-v2.1.0/heimdall/source/HelpAction.cpp000066400000000000000000000026411464356164000212440ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Heimdall #include "Heimdall.h" #include "HelpAction.h" #include "Interface.h" using namespace Heimdall; const char *HelpAction::usage = "Action: help\n\ Description: Displays this dialogue.\n"; int HelpAction::Execute(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) { Interface::PrintUsage(); return (0); } Heimdall-v2.1.0/heimdall/source/HelpAction.h000066400000000000000000000023701464356164000207100ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef HELPACTION_H #define HELPACTION_H namespace Heimdall { namespace HelpAction { extern const char *usage; int Execute(int argc, char **argv); } } #endif Heimdall-v2.1.0/heimdall/source/InboundPacket.h000066400000000000000000000041631464356164000214120ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef INBOUNDPACKET_H #define INBOUNDPACKET_H // Heimdall #include "Packet.h" namespace Heimdall { class InboundPacket : public Packet { private: bool sizeVariable; unsigned int receivedSize; protected: unsigned int UnpackInteger(unsigned int offset) const { #ifdef WORDS_BIGENDIAN unsigned int value = (data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3]; #else // Flip endianness unsigned int value = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24); #endif return (value); } public: InboundPacket(unsigned int size, bool sizeVariable = false) : Packet(size) { this->sizeVariable = sizeVariable; } bool IsSizeVariable(void) const { return (sizeVariable); } unsigned int GetReceivedSize(void) const { return (receivedSize); } void SetReceivedSize(unsigned int receivedSize) { this->receivedSize = receivedSize; } virtual bool Unpack(void) = 0; }; } #endif Heimdall-v2.1.0/heimdall/source/InfoAction.cpp000066400000000000000000000026611464356164000212510ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Heimdall #include "Heimdall.h" #include "InfoAction.h" #include "Interface.h" using namespace Heimdall; const char *InfoAction::usage = "Action: info\n\ Description: Displays information about Heimdall.\n"; int InfoAction::Execute(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) { Interface::PrintFullInfo(); return (0); } Heimdall-v2.1.0/heimdall/source/InfoAction.h000066400000000000000000000023701464356164000207130ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef INFOACTION_H #define INFOACTION_H namespace Heimdall { namespace InfoAction { extern const char *usage; int Execute(int argc, char **argv); } } #endif Heimdall-v2.1.0/heimdall/source/Interface.cpp000066400000000000000000000210141464356164000211110ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // C/C++ Standard Library #include #include #include // Heimdall #include "ClosePcScreenAction.h" #include "DetectAction.h" #include "DownloadPitAction.h" #include "FlashAction.h" #include "HelpAction.h" #include "InfoAction.h" #include "Heimdall.h" #include "Interface.h" #include "PrintPitAction.h" #include "VersionAction.h" using namespace std; using namespace libpit; using namespace Heimdall; map actionMap; bool stdoutErrors = false; const char *version = "v2.1.0"; const char *actionUsage = "Usage: heimdall \n"; const char *releaseInfo = "Heimdall %s\n\n\ Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna https://glassechidna.com.au\n\ Copyright (c) 2021-2024 Henrik Grimler\n\ This software is provided free of charge. Copying and redistribution is encouraged.\n\n"; static const char *extraInfo = "Heimdall utilises libusb for all USB communication:\n\ https://www.libusb.info/\n\ \n\ libusb is licensed under the LGPL-2.1:\n\ https://www.gnu.org/licenses/licenses.html#LGPL\n\n"; void populateActionMap(void) { actionMap["close-pc-screen"] = Interface::ActionInfo(&ClosePcScreenAction::Execute, ClosePcScreenAction::usage); actionMap["detect"] = Interface::ActionInfo(&DetectAction::Execute, DetectAction::usage); actionMap["download-pit"] = Interface::ActionInfo(&DownloadPitAction::Execute, DownloadPitAction::usage); actionMap["flash"] = Interface::ActionInfo(&FlashAction::Execute, FlashAction::usage); actionMap["help"] = Interface::ActionInfo(&HelpAction::Execute, HelpAction::usage); actionMap["info"] = Interface::ActionInfo(&InfoAction::Execute, InfoAction::usage); actionMap["print-pit"] = Interface::ActionInfo(&PrintPitAction::Execute, PrintPitAction::usage); actionMap["version"] = Interface::ActionInfo(&VersionAction::Execute, VersionAction::usage); } const map& Interface::GetActionMap(void) { if (actionMap.size() == 0) populateActionMap(); return actionMap; } void Interface::Print(const char *format, ...) { va_list args; va_start(args, format); vfprintf(stdout, format, args); fflush(stdout); va_end(args); } void Interface::PrintWarning(const char *format, ...) { va_list stderrArgs; va_start(stderrArgs, format); if (stdoutErrors) { va_list stdoutArgs; va_copy(stdoutArgs, stderrArgs); fprintf(stdout, "WARNING: "); vfprintf(stdout, format, stdoutArgs); fflush(stdout); va_end(stdoutArgs); } fprintf(stderr, "WARNING: "); vfprintf(stderr, format, stderrArgs); fflush(stderr); va_end(stderrArgs); } void Interface::PrintWarningSameLine(const char *format, ...) { va_list stderrArgs; va_start(stderrArgs, format); if (stdoutErrors) { va_list stdoutArgs; va_copy(stdoutArgs, stderrArgs); vfprintf(stdout, format, stdoutArgs); fflush(stdout); va_end(stdoutArgs); } vfprintf(stderr, format, stderrArgs); fflush(stderr); va_end(stderrArgs); } void Interface::PrintError(const char *format, ...) { va_list stderrArgs; va_start(stderrArgs, format); if (stdoutErrors) { va_list stdoutArgs; va_copy(stdoutArgs, stderrArgs); fprintf(stdout, "ERROR: "); vfprintf(stdout, format, stdoutArgs); fflush(stdout); va_end(stdoutArgs); } fprintf(stderr, "ERROR: "); vfprintf(stderr, format, stderrArgs); fflush(stderr); va_end(stderrArgs); } void Interface::PrintErrorSameLine(const char *format, ...) { va_list stderrArgs; va_start(stderrArgs, format); if (stdoutErrors) { va_list stdoutArgs; va_copy(stdoutArgs, stderrArgs); vfprintf(stdout, format, stdoutArgs); fflush(stdout); va_end(stdoutArgs); } vfprintf(stderr, format, stderrArgs); fflush(stderr); va_end(stderrArgs); } void Interface::PrintVersion(void) { Interface::Print("%s\n", version); } void Interface::PrintUsage(void) { const map& actionMap = Interface::GetActionMap(); Interface::Print(actionUsage); for (map::const_iterator it = actionMap.begin(); it != actionMap.end(); it++) Interface::Print("\n%s", it->second.usage); } void Interface::PrintReleaseInfo(void) { Interface::Print(releaseInfo, version); } void Interface::PrintFullInfo(void) { Interface::Print(releaseInfo, version); Interface::Print(extraInfo); } void Interface::PrintDeviceDetectionFailed(void) { Interface::PrintError("Failed to detect compatible download-mode device.\n"); } void Interface::PrintPit(const PitData *pitData) { Interface::Print("--- PIT Header ---\n"); Interface::Print("Entry Count: %d\n", pitData->GetEntryCount()); Interface::Print("Unknown string: %s\n", pitData->GetComTar2()); Interface::Print("CPU/bootloader tag: %s\n", pitData->GetCpuBlId()); Interface::Print("Logic unit count: %d\n", pitData->GetLUCount()); for (unsigned int i = 0; i < pitData->GetEntryCount(); i++) { const PitEntry *entry = pitData->GetEntry(i); Interface::Print("\n\n--- Entry #%d ---\n", i); Interface::Print("Binary Type: %d (", entry->GetBinaryType()); switch (entry->GetBinaryType()) { case PitEntry::kBinaryTypeApplicationProcessor: Interface::Print("AP"); break; case PitEntry::kBinaryTypeCommunicationProcessor: Interface::Print("CP"); break; default: Interface::Print("Unknown"); break; } Interface::Print(")\n"); Interface::Print("Device Type: %d (", entry->GetDeviceType()); switch (entry->GetDeviceType()) { case PitEntry::kDeviceTypeOneNand: Interface::Print("OneNAND"); break; case PitEntry::kDeviceTypeFile: Interface::Print("File/FAT"); break; case PitEntry::kDeviceTypeMMC: Interface::Print("MMC"); break; case PitEntry::kDeviceTypeAll: Interface::Print("All (?)"); break; case PitEntry::kDeviceTypeUFS: Interface::Print("UFS"); break; default: Interface::Print("Unknown"); break; } Interface::Print(")\n"); Interface::Print("Identifier: %d\n", entry->GetIdentifier()); Interface::Print("Attributes: %d (", entry->GetAttributes()); if (entry->GetAttributes() & PitEntry::kAttributeSTL) Interface::Print("STL "); /*if (entry->GetAttributes() & PitEntry::kAttributeBML) Interface::Print("BML ");*/ if (entry->GetAttributes() & PitEntry::kAttributeWrite) Interface::Print("Read/Write"); else Interface::Print("Read-Only"); Interface::Print(")\n"); Interface::Print("Update Attributes: %d", entry->GetUpdateAttributes()); if (entry->GetUpdateAttributes()) { Interface::Print(" ("); if (entry->GetUpdateAttributes() & PitEntry::kUpdateAttributeFota) { if (entry->GetUpdateAttributes() & PitEntry::kUpdateAttributeSecure) Interface::Print("FOTA, Secure"); else Interface::Print("FOTA"); } else { if (entry->GetUpdateAttributes() & PitEntry::kUpdateAttributeSecure) Interface::Print("Secure"); } Interface::Print(")\n"); } else { Interface::Print("\n"); } Interface::Print("Partition Block Size/Offset: %d\n", entry->GetBlockSizeOrOffset()); Interface::Print("Partition Block Count: %d\n", entry->GetBlockCount()); Interface::Print("File Offset (Obsolete): %d\n", entry->GetFileOffset()); Interface::Print("File Size (Obsolete): %d\n", entry->GetFileSize()); Interface::Print("Partition Name: %s\n", entry->GetPartitionName()); Interface::Print("Flash Filename: %s\n", entry->GetFlashFilename()); Interface::Print("FOTA Filename: %s\n", entry->GetFotaFilename()); } Interface::Print("\n"); } void Interface::SetStdoutErrors(bool enabled) { stdoutErrors = enabled; } Heimdall-v2.1.0/heimdall/source/Interface.h000066400000000000000000000043371464356164000205670ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef INTERFACE_H #define INTERFACE_H // C/C++ Standard Library #include #include // libpit #include "libpit.h" // Heimdall #include "Heimdall.h" namespace Heimdall { namespace Interface { typedef int (*ActionExecuteFunction)(int, char **); typedef struct ActionInfo { ActionExecuteFunction executeFunction; const char *usage; ActionInfo() { executeFunction = nullptr; usage = nullptr; } ActionInfo(ActionExecuteFunction executeFunction, const char *usage) { this->executeFunction = executeFunction; this->usage = usage; } } ActionInfo; const std::map& GetActionMap(void); void Print(const char *format, ...); void PrintWarning(const char *format, ...); void PrintWarningSameLine(const char *format, ...); void PrintError(const char *format, ...); void PrintErrorSameLine(const char *format, ...); void PrintVersion(void); void PrintUsage(void); void PrintReleaseInfo(void); void PrintFullInfo(void); void PrintDeviceDetectionFailed(void); void PrintPit(const libpit::PitData *pitData); void SetStdoutErrors(bool enabled); } } #endif Heimdall-v2.1.0/heimdall/source/OutboundPacket.h000066400000000000000000000042151464356164000216110ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef OUTBOUNDPACKET_H #define OUTBOUNDPACKET_H // Heimdall #include "Packet.h" namespace Heimdall { class OutboundPacket : public Packet { protected: void PackInteger(unsigned int offset, unsigned int value) { #ifdef WORDS_BIGENDIAN data[offset] = (value & 0xFF000000) >> 24; data[offset + 1] = (value & 0x00FF0000) >> 16; data[offset + 2] = (value & 0x0000FF00) >> 8; data[offset + 3] = value & 0x000000FF; #else // Flip endianness data[offset] = value & 0x000000FF; data[offset + 1] = (value & 0x0000FF00) >> 8; data[offset + 2] = (value & 0x00FF0000) >> 16; data[offset + 3] = (value & 0xFF000000) >> 24; #endif } void PackShort(unsigned int offset, unsigned short value) { #ifdef WORDS_BIGENDIAN data[offset] = (value & 0xFF00) >> 8; data[offset + 1] = value & 0x00FF; #else // Flip endianness data[offset] = value & 0x00FF; data[offset + 1] = (value & 0xFF00) >> 8; #endif } public: OutboundPacket(unsigned int size) : Packet(size) { } virtual void Pack(void) = 0; }; } #endif Heimdall-v2.1.0/heimdall/source/Packet.h000066400000000000000000000031301464356164000200640ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef PACKET_H #define PACKET_H // C++ Standard Library #include namespace Heimdall { class Packet { private: unsigned int size; protected: unsigned char *data; public: Packet(unsigned int size) { this->size = size; data = new unsigned char[size]; memset(data, 0, size); } virtual ~Packet() { delete [] data; } unsigned int GetSize(void) const { return (size); } unsigned char *GetData(void) { return (data); } }; } #endif Heimdall-v2.1.0/heimdall/source/PitFilePacket.h000066400000000000000000000035031464356164000213450ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef PITFILEPACKET_H #define PITFILEPACKET_H // Heimdall #include "ControlPacket.h" namespace Heimdall { class PitFilePacket : public ControlPacket { public: enum { kRequestFlash = 0x00, kRequestDump = 0x01, kRequestPart = 0x02, kRequestEndTransfer = 0x03 }; protected: enum { kDataSize = ControlPacket::kDataSize + 4 }; private: unsigned int request; public: PitFilePacket(unsigned int request) : ControlPacket(ControlPacket::kControlTypePitFile) { this->request = request; } unsigned int GetRequest(void) const { return (request); } void Pack(void) { ControlPacket::Pack(); PackInteger(ControlPacket::kDataSize, request); } }; } #endif Heimdall-v2.1.0/heimdall/source/PitFileResponse.h000066400000000000000000000031721464356164000217360ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef PITFILERESPONSE_H #define PITFILERESPONSE_H // Heimdall #include "ResponsePacket.h" namespace Heimdall { class PitFileResponse : public ResponsePacket { private: unsigned int fileSize; public: PitFileResponse() : ResponsePacket(ResponsePacket::kResponseTypePitFile) { } unsigned int GetFileSize(void) const { return (fileSize); } bool Unpack(void) { if (!ResponsePacket::Unpack()) return (false); fileSize = UnpackInteger(ResponsePacket::kDataSize); return (true); } }; } #endif Heimdall-v2.1.0/heimdall/source/PrintPitAction.cpp000066400000000000000000000140121464356164000221200ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // C Standard Library #include // Heimdall #include "Arguments.h" #include "BridgeManager.h" #include "Heimdall.h" #include "Interface.h" #include "PrintPitAction.h" using namespace std; using namespace libpit; using namespace Heimdall; const char *PrintPitAction::usage = "Action: print-pit\n\ Arguments: [--file ] [--verbose] [--no-reboot] [--stdout-errors]\n\ [--usb-log-level ] [--wait]\n\ Description: Prints the contents of a PIT file in a human readable format. If\n\ a filename is not provided then Heimdall retrieves the PIT file from the \n\ connected device. If --wait is used then Heimdall waits until a compatible\n\ device is connected.\n\ Note: --no-reboot causes the device to remain in download mode after the action\n\ is completed. If you wish to perform another action whilst remaining in\n\ download mode, then the following action must specify the --resume flag.\n"; int PrintPitAction::Execute(int argc, char **argv) { // Handle arguments map argumentTypes; argumentTypes["file"] = kArgumentTypeString; argumentTypes["no-reboot"] = kArgumentTypeFlag; argumentTypes["resume"] = kArgumentTypeFlag; argumentTypes["verbose"] = kArgumentTypeFlag; argumentTypes["wait"] = kArgumentTypeFlag; argumentTypes["stdout-errors"] = kArgumentTypeFlag; argumentTypes["usb-log-level"] = kArgumentTypeString; Arguments arguments(argumentTypes); if (!arguments.ParseArguments(argc, argv, 2)) { Interface::Print(PrintPitAction::usage); return (0); } const StringArgument *fileArgument = static_cast(arguments.GetArgument("file")); bool reboot = arguments.GetArgument("no-reboot") == nullptr; bool resume = arguments.GetArgument("resume") != nullptr; bool verbose = arguments.GetArgument("verbose") != nullptr; bool waitForDevice = arguments.GetArgument("wait") != nullptr; if (arguments.GetArgument("stdout-errors") != nullptr) Interface::SetStdoutErrors(true); const StringArgument *usbLogLevelArgument = static_cast(arguments.GetArgument("usb-log-level")); BridgeManager::UsbLogLevel usbLogLevel = BridgeManager::UsbLogLevel::Default; if (usbLogLevelArgument) { const string& usbLogLevelString = usbLogLevelArgument->GetValue(); if (usbLogLevelString.compare("none") == 0 || usbLogLevelString.compare("NONE") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::None; } else if (usbLogLevelString.compare("error") == 0 || usbLogLevelString.compare("ERROR") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Error; } else if (usbLogLevelString.compare("warning") == 0 || usbLogLevelString.compare("WARNING") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Warning; } else if (usbLogLevelString.compare("info") == 0 || usbLogLevelString.compare("INFO") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Info; } else if (usbLogLevelString.compare("debug") == 0 || usbLogLevelString.compare("DEBUG") == 0) { usbLogLevel = BridgeManager::UsbLogLevel::Debug; } else { Interface::Print("Unknown USB log level: %s\n\n", usbLogLevelString.c_str()); Interface::Print(PrintPitAction::usage); return (0); } } // Open file (if specified). FILE *localPitFile = nullptr; if (fileArgument) { const char *filename = fileArgument->GetValue().c_str(); localPitFile = FileOpen(filename, "rb"); if (!localPitFile) { Interface::PrintError("Failed to open file \"%s\"\n", filename); return (1); } } // Info Interface::PrintReleaseInfo(); Sleep(1000); if (localPitFile) { // Print PIT from file; there's no need for a BridgeManager. FileSeek(localPitFile, 0, SEEK_END); unsigned int localPitFileSize = (unsigned int)FileTell(localPitFile); FileRewind(localPitFile); // Load the local pit file into memory. unsigned char *pitFileBuffer = new unsigned char[localPitFileSize]; (void)fread(pitFileBuffer, 1, localPitFileSize, localPitFile); FileClose(localPitFile); PitData *pitData = new PitData(); pitData->Unpack(pitFileBuffer); delete [] pitFileBuffer; Interface::PrintPit(pitData); delete pitData; return (0); } else { // Print PIT from a device. BridgeManager *bridgeManager = new BridgeManager(verbose, waitForDevice); bridgeManager->SetUsbLogLevel(usbLogLevel); if (bridgeManager->Initialise(resume) != BridgeManager::kInitialiseSucceeded || !bridgeManager->BeginSession()) { delete bridgeManager; return (1); } unsigned char *devicePit; bool success = bridgeManager->DownloadPitFile(&devicePit) != 0; if (success) { PitData *pitData = new PitData(); if (pitData->Unpack(devicePit)) { Interface::PrintPit(pitData); } else { Interface::PrintError("Failed to unpack device's PIT file!\n"); success = false; } delete pitData; } delete [] devicePit; if (!bridgeManager->EndSession(reboot)) success = false; delete bridgeManager; return (success ? 0 : 1); } } Heimdall-v2.1.0/heimdall/source/PrintPitAction.h000066400000000000000000000024041464356164000215670ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef PRINTPITACTION_H #define PRINTPITACTION_H namespace Heimdall { namespace PrintPitAction { extern const char *usage; int Execute(int argc, char **argv); } } #endif Heimdall-v2.1.0/heimdall/source/ReceiveFilePartPacket.h000066400000000000000000000026761464356164000230340ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef RECEIVEFILEPARTPACKET_H #define RECEIVEFILEPARTPACKET_H // Heimdall #include "InboundPacket.h" namespace Heimdall { class ReceiveFilePartPacket : public InboundPacket { public: enum { kDataSize = 500 }; ReceiveFilePartPacket() : InboundPacket(kDataSize, true) { } bool Unpack(void) { return (true); } }; } #endif Heimdall-v2.1.0/heimdall/source/ResponsePacket.h000066400000000000000000000037451464356164000216170ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef RESPONSEPACKET_H #define RESPONSEPACKET_H // Heimdall #include "InboundPacket.h" namespace Heimdall { class ResponsePacket : public InboundPacket { public: enum { kResponseTypeSendFilePart = 0x00, kResponseTypeSessionSetup = 0x64, kResponseTypePitFile = 0x65, kResponseTypeFileTransfer = 0x66, kResponseTypeEndSession = 0x67 }; private: unsigned int responseType; protected: enum { kDataSize = 4 }; public: ResponsePacket(int responseType) : InboundPacket(8) { this->responseType = responseType; } unsigned int GetResponseType(void) const { return (responseType); } virtual bool Unpack(void) { unsigned int receivedResponseType = UnpackInteger(0); if (receivedResponseType != responseType) { responseType = receivedResponseType; return (false); } return (true); } }; } #endif Heimdall-v2.1.0/heimdall/source/SendFilePartPacket.h000066400000000000000000000036721464356164000223400ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef SENDFILEPARTPACKET_H #define SENDFILEPARTPACKET_H // C Standard Library #include #include // Heimdall #include "Packet.h" namespace Heimdall { class SendFilePartPacket : public OutboundPacket { public: SendFilePartPacket(FILE *file, unsigned long size) : OutboundPacket(size) { memset(data, 0, size); unsigned long position = (unsigned long)FileTell(file); FileSeek(file, 0, SEEK_END); unsigned long fileSize = (unsigned long)FileTell(file); FileSeek(file, position, SEEK_SET); // min(fileSize, size) unsigned long bytesToRead = (fileSize < size) ? fileSize - position : size; (void)fread(data, 1, bytesToRead, file); } SendFilePartPacket(unsigned char *buffer, unsigned int size) : OutboundPacket(size) { memcpy(data, buffer, size); } void Pack(void) { } }; } #endif Heimdall-v2.1.0/heimdall/source/SendFilePartResponse.h000066400000000000000000000032331464356164000227200ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef SENDFILEPARTRESPONSE_H #define SENDFILEPARTRESPONSE_H // Heimdall #include "ResponsePacket.h" namespace Heimdall { class SendFilePartResponse : public ResponsePacket { private: unsigned int partIndex; public: SendFilePartResponse() : ResponsePacket(ResponsePacket::kResponseTypeSendFilePart) { } unsigned int GetPartIndex(void) const { return (partIndex); } bool Unpack(void) { if (!ResponsePacket::Unpack()) return (false); partIndex = UnpackInteger(ResponsePacket::kDataSize); return (true); } }; } #endif Heimdall-v2.1.0/heimdall/source/SessionSetupPacket.h000066400000000000000000000035711464356164000224620ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef SESSIONSETUPPACKET_H #define SESSIONSETUPPACKET_H // Heimdall #include "ControlPacket.h" namespace Heimdall { class SessionSetupPacket : public ControlPacket { public: enum { kBeginSession = 0, kDeviceType = 1, // ? kTotalBytes = 2, //kEnableSomeSortOfFlag = 3, kFilePartSize = 5, kEnableTFlash = 8 }; private: unsigned int request; protected: enum { kDataSize = ControlPacket::kDataSize + 4 }; public: SessionSetupPacket(unsigned int request) : ControlPacket(ControlPacket::kControlTypeSession) { this->request = request; } unsigned int GetRequest(void) const { return (request); } void Pack(void) { ControlPacket::Pack(); PackInteger(ControlPacket::kDataSize, request); } }; } #endif Heimdall-v2.1.0/heimdall/source/SessionSetupResponse.h000066400000000000000000000032131464356164000230420ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef SESSIONSETUPRESPONSE_H #define SESSIONSETUPRESPONSE_H // Heimdall #include "ResponsePacket.h" namespace Heimdall { class SessionSetupResponse : public ResponsePacket { private: unsigned int result; public: SessionSetupResponse() : ResponsePacket(ResponsePacket::kResponseTypeSessionSetup) { } unsigned int GetResult(void) const { return (result); } bool Unpack(void) { if (!ResponsePacket::Unpack()) return (false); result = UnpackInteger(ResponsePacket::kDataSize); return (true); } }; } #endif Heimdall-v2.1.0/heimdall/source/SetupSessionPacket.h000066400000000000000000000037721464356164000224650ustar00rootroot00000000000000/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef SETUPSESSIONPACKET_H #define SETUPSESSIONPACKET_H // Heimdall #include "ControlPacket.h" namespace Heimdall { class SetupSessionPacket : public ControlPacket { public: enum { kBeginSession = 0, kDeviceInfo = 1, kTotalBytes = 2 }; private: unsigned int request; unsigned int unknown3Parameter; public: SetupSessionPacket(unsigned int request, unsigned int unknown3Parameter = 0) : ControlPacket(ControlPacket::kControlTypeSetupSession) { this->request = request; this->unknown3Parameter = unknown3Parameter; } unsigned int GetRequest(void) const { return (request); } unsigned int GetUnknown3Parameter(void) const { return (unknown3Parameter); } void Pack(void) { ControlPacket::Pack(); PackInteger(ControlPacket::kDataSize, request); PackInteger(ControlPacket::kDataSize + 4, unknown3Parameter); } }; } #endif Heimdall-v2.1.0/heimdall/source/SetupSessionResponse.h000066400000000000000000000032171464356164000230460ustar00rootroot00000000000000/* Copyright (c) 2010-2012 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef SETUPSESSIONRESPONSE_H #define SETUPSESSIONRESPONSE_H // Heimdall #include "ResponsePacket.h" namespace Heimdall { class SetupSessionResponse : public ResponsePacket { private: unsigned int unknown; public: SetupSessionResponse() : ResponsePacket(ResponsePacket::kResponseTypeBeginSession) { } unsigned int GetUnknown(void) const { return (unknown); } bool Unpack(void) { if (!ResponsePacket::Unpack()) return (false); unknown = UnpackInteger(ResponsePacket::kDataSize); return (true); } }; } #endif Heimdall-v2.1.0/heimdall/source/TotalBytesPacket.h000066400000000000000000000033761464356164000221130ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef TOTALBYTESPACKET_H #define TOTALBYTESPACKET_H // C++ Standard Library #include // Heimdall #include "SessionSetupPacket.h" namespace Heimdall { class TotalBytesPacket : public SessionSetupPacket { private: uint64_t totalBytes; public: TotalBytesPacket(uint64_t totalBytes) : SessionSetupPacket(SessionSetupPacket::kTotalBytes) { this->totalBytes = totalBytes; } uint64_t GetTotalBytes(void) const { return (totalBytes); } void Pack(void) { SessionSetupPacket::Pack(); PackInteger(SessionSetupPacket::kDataSize, totalBytes); PackInteger(SessionSetupPacket::kDataSize + 4, totalBytes>>32); } }; } #endif Heimdall-v2.1.0/heimdall/source/Utility.cpp000066400000000000000000000045451464356164000206660ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // C/C++ Standard Library #include #include #include // Heimdall #include "Heimdall.h" #include "Utility.h" using namespace Heimdall; NumberParsingStatus Utility::ParseInt(int &intValue, const char *string, int base) { errno = 0; char *end; long longValue = strtol(string, &end, base); if (*string == '\0' || *end != '\0') { return (kNumberParsingStatusInconvertible); } else if (errno == ERANGE) { intValue = (longValue == LONG_MAX) ? INT_MAX : INT_MIN; return (kNumberParsingStatusRangeError); } else if (longValue > INT_MAX) { intValue = INT_MAX; return (kNumberParsingStatusRangeError); } else if (longValue < INT_MIN) { intValue = INT_MIN; return (kNumberParsingStatusRangeError); } intValue = longValue; return (kNumberParsingStatusSuccess); } NumberParsingStatus Utility::ParseUnsignedInt(unsigned int &uintValue, const char *string, int base) { errno = 0; char *end; unsigned long ulongValue = strtoul(string, &end, base); if (*string == '\0' || *end != '\0') { return kNumberParsingStatusInconvertible; } else if (errno == ERANGE || ulongValue > INT_MAX) { uintValue = UINT_MAX; return (kNumberParsingStatusRangeError); } uintValue = ulongValue; return (kNumberParsingStatusSuccess); } Heimdall-v2.1.0/heimdall/source/Utility.h000066400000000000000000000027631464356164000203330ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef UTILITY_H #define UTILITY_H namespace Heimdall { typedef enum { kNumberParsingStatusSuccess = 0, kNumberParsingStatusRangeError, kNumberParsingStatusInconvertible } NumberParsingStatus; namespace Utility { NumberParsingStatus ParseInt(int &intValue, const char *string, int base = 0); NumberParsingStatus ParseUnsignedInt(unsigned int &uintValue, const char *string, int base = 0); } } #endif Heimdall-v2.1.0/heimdall/source/VersionAction.cpp000066400000000000000000000027031464356164000220000ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // Heimdall #include "Heimdall.h" #include "Interface.h" #include "VersionAction.h" using namespace Heimdall; const char *VersionAction::usage = "Action: version\n\ Description: Displays the version number of this binary.\n"; int VersionAction::Execute(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) { Interface::PrintVersion(); return (0); } Heimdall-v2.1.0/heimdall/source/VersionAction.h000066400000000000000000000024011464356164000214400ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef VERSIONACTION_H #define VERSIONACTION_H namespace Heimdall { namespace VersionAction { extern const char *usage; int Execute(int argc, char **argv); } } #endif Heimdall-v2.1.0/heimdall/source/main.cpp000066400000000000000000000033421464356164000201410ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // C/C++ Standard Library #include #include #include #include // libpit #include "libpit.h" // Heimdall #include "Heimdall.h" #include "HelpAction.h" #include "Interface.h" using namespace std; using namespace Heimdall; int main(int argc, char **argv) { if (argc < 2) { Interface::PrintUsage(); return (0); } int result = 0; map::const_iterator actionIt = Interface::GetActionMap().find(argv[1]); if (actionIt != Interface::GetActionMap().end()) result = actionIt->second.executeFunction(argc, argv); else result = HelpAction::Execute(argc, argv); return (result); } Heimdall-v2.1.0/libpit/000077500000000000000000000000001464356164000147135ustar00rootroot00000000000000Heimdall-v2.1.0/libpit/CMakeLists.txt000066400000000000000000000003071464356164000174530ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(libpit) set(LIBPIT_SOURCE_FILES source/libpit.cpp) add_library(pit STATIC ${LIBPIT_SOURCE_FILES}) target_compile_features(pit PRIVATE cxx_std_11) Heimdall-v2.1.0/libpit/source/000077500000000000000000000000001464356164000162135ustar00rootroot00000000000000Heimdall-v2.1.0/libpit/source/libpit.cpp000066400000000000000000000173601464356164000202110ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ // libpit #include "libpit.h" using namespace libpit; PitEntry::PitEntry() { binaryType = false; deviceType = 0; identifier = 0; attributes = 0; updateAttributes = 0; blockSizeOrOffset = 0; blockCount = 0; fileOffset = 0; fileSize = 0; memset(partitionName, 0, PitEntry::kPartitionNameMaxLength); memset(flashFilename, 0, PitEntry::kFlashFilenameMaxLength); memset(fotaFilename, 0, PitEntry::kFotaFilenameMaxLength); } PitEntry::~PitEntry() { } bool PitEntry::Matches(const PitEntry *otherPitEntry) const { if (binaryType == otherPitEntry->binaryType && deviceType == otherPitEntry->deviceType && identifier == otherPitEntry->identifier && attributes == otherPitEntry->attributes && updateAttributes == otherPitEntry->updateAttributes && blockSizeOrOffset == otherPitEntry->blockSizeOrOffset && blockCount == otherPitEntry->blockCount && fileOffset == otherPitEntry->fileOffset && fileSize == otherPitEntry->fileSize && strcmp(partitionName, otherPitEntry->partitionName) == 0 && strcmp(flashFilename, otherPitEntry->flashFilename) == 0 && strcmp(fotaFilename, otherPitEntry->fotaFilename) == 0) { return (true); } else { return (false); } } PitData::PitData() { entryCount = 0; com_tar2[0] = '\0'; cpu_bl_id[0] = '\0'; luCount = 0; } PitData::~PitData() { for (unsigned int i = 0; i < entries.size(); i++) delete entries[i]; } bool PitData::Unpack(const unsigned char *data) { if (PitData::UnpackInteger(data, 0) != PitData::kFileIdentifier) return (false); // Remove existing entries for (unsigned int i = 0; i < entries.size(); i++) delete entries[i]; entryCount = PitData::UnpackInteger(data, 4); entries.resize(entryCount); if (!memcpy(com_tar2, &data[8], 8)) return (false); com_tar2[8]='\0'; if (!memcpy(cpu_bl_id, &data[16], 8)) return (false); cpu_bl_id[8]='\0'; luCount = PitData::UnpackShort(data, 24); unsigned int integerValue; unsigned int entryOffset; for (unsigned int i = 0; i < entryCount; i++) { entryOffset = PitData::kHeaderDataSize + i * PitEntry::kDataSize; entries[i] = new PitEntry(); integerValue = PitData::UnpackInteger(data, entryOffset); entries[i]->SetBinaryType(integerValue); integerValue = PitData::UnpackInteger(data, entryOffset + 4); entries[i]->SetDeviceType(integerValue); integerValue = PitData::UnpackInteger(data, entryOffset + 8); entries[i]->SetIdentifier(integerValue); integerValue = PitData::UnpackInteger(data, entryOffset + 12); entries[i]->SetAttributes(integerValue); integerValue = PitData::UnpackInteger(data, entryOffset + 16); entries[i]->SetUpdateAttributes(integerValue); integerValue = PitData::UnpackInteger(data, entryOffset + 20); entries[i]->SetBlockSizeOrOffset(integerValue); integerValue = PitData::UnpackInteger(data, entryOffset + 24); entries[i]->SetBlockCount(integerValue); integerValue = PitData::UnpackInteger(data, entryOffset + 28); entries[i]->SetFileOffset(integerValue); integerValue = PitData::UnpackInteger(data, entryOffset + 32); entries[i]->SetFileSize(integerValue); entries[i]->SetPartitionName((const char *)data + entryOffset + 36); entries[i]->SetFlashFilename((const char *)data + entryOffset + 36 + PitEntry::kPartitionNameMaxLength); entries[i]->SetFotaFilename((const char *)data + entryOffset + 36 + PitEntry::kPartitionNameMaxLength + PitEntry::kFlashFilenameMaxLength); } return (true); } void PitData::Pack(unsigned char *data) const { PitData::PackInteger(data, 0, PitData::kFileIdentifier); PitData::PackInteger(data, 4, entryCount); memcpy(&data[8], com_tar2, 8); memcpy(&data[16], cpu_bl_id, 8); PitData::PackShort(data, 24, luCount); int entryOffset; for (unsigned int i = 0; i < entryCount; i++) { entryOffset = PitData::kHeaderDataSize + i * PitEntry::kDataSize; PitData::PackInteger(data, entryOffset, entries[i]->GetBinaryType()); PitData::PackInteger(data, entryOffset + 4, entries[i]->GetDeviceType()); PitData::PackInteger(data, entryOffset + 8, entries[i]->GetIdentifier()); PitData::PackInteger(data, entryOffset + 12, entries[i]->GetAttributes()); PitData::PackInteger(data, entryOffset + 16, entries[i]->GetUpdateAttributes()); PitData::PackInteger(data, entryOffset + 20, entries[i]->GetBlockSizeOrOffset()); PitData::PackInteger(data, entryOffset + 24, entries[i]->GetBlockCount()); PitData::PackInteger(data, entryOffset + 28, entries[i]->GetFileOffset()); PitData::PackInteger(data, entryOffset + 32, entries[i]->GetFileSize()); memcpy(data + entryOffset + 36, entries[i]->GetPartitionName(), PitEntry::kPartitionNameMaxLength); memcpy(data + entryOffset + 36 + PitEntry::kPartitionNameMaxLength, entries[i]->GetFlashFilename(), PitEntry::kFlashFilenameMaxLength); memcpy(data + entryOffset + 36 + PitEntry::kPartitionNameMaxLength + PitEntry::kFlashFilenameMaxLength, entries[i]->GetFotaFilename(), PitEntry::kFotaFilenameMaxLength); } } bool PitData::Matches(const PitData *otherPitData) const { if (entryCount == otherPitData->entryCount && (strncmp(com_tar2, otherPitData->com_tar2, 8) == 0) && (strncmp(cpu_bl_id, otherPitData->cpu_bl_id, 8) == 0) && luCount == otherPitData->luCount) { for (unsigned int i = 0; i < entryCount; i++) { if (!entries[i]->Matches(otherPitData->entries[i])) return (false); } return (true); } else { return (false); } } void PitData::Clear(void) { entryCount = 0; com_tar2[0] = '\0'; cpu_bl_id[0] = '\0'; luCount = 0; for (unsigned int i = 0; i < entries.size(); i++) delete entries[i]; entries.clear(); } PitEntry *PitData::GetEntry(unsigned int index) { return (entries[index]); } const PitEntry *PitData::GetEntry(unsigned int index) const { return (entries[index]); } PitEntry *PitData::FindEntry(const char *partitionName) { for (unsigned int i = 0; i < entries.size(); i++) { if (entries[i]->IsFlashable() && strcmp(entries[i]->GetPartitionName(), partitionName) == 0) return (entries[i]); } return (nullptr); } const PitEntry *PitData::FindEntry(const char *partitionName) const { for (unsigned int i = 0; i < entries.size(); i++) { if (entries[i]->IsFlashable() && strcmp(entries[i]->GetPartitionName(), partitionName) == 0) return (entries[i]); } return (nullptr); } PitEntry *PitData::FindEntry(unsigned int partitionIdentifier) { for (unsigned int i = 0; i < entries.size(); i++) { if (entries[i]->IsFlashable() && entries[i]->GetIdentifier() == partitionIdentifier) return (entries[i]); } return (nullptr); } const PitEntry *PitData::FindEntry(unsigned int partitionIdentifier) const { for (unsigned int i = 0; i < entries.size(); i++) { if (entries[i]->IsFlashable() && entries[i]->GetIdentifier() == partitionIdentifier) return (entries[i]); } return (nullptr); } Heimdall-v2.1.0/libpit/source/libpit.h000066400000000000000000000214351464356164000176540ustar00rootroot00000000000000/* Copyright (c) 2010-2017 Benjamin Dobell, Glass Echidna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ #ifndef LIBPIT_H #define LIBPIT_H #ifdef WIN32 #pragma warning(disable : 4996) #endif #if defined(_MSC_VER) && (_MSC_VER < 1700) # ifndef nullptr # define nullptr 0 # endif #endif // C/C++ Standard Library #include #include #include namespace libpit { class PitEntry { public: enum { kDataSize = 132, kPartitionNameMaxLength = 32, kFlashFilenameMaxLength = 32, kFotaFilenameMaxLength = 32 }; enum { kBinaryTypeApplicationProcessor = 0, kBinaryTypeCommunicationProcessor = 1 }; enum { kDeviceTypeOneNand = 0, kDeviceTypeFile, // FAT kDeviceTypeMMC, kDeviceTypeAll, // ? kDeviceTypeUFS = 8 }; enum { kAttributeWrite = 1, kAttributeSTL = 1 << 1 /* kAttributeBML = 1 << 2 */ // ??? }; enum { kUpdateAttributeFota = 1, kUpdateAttributeSecure = 1 << 1 }; private: unsigned int binaryType; unsigned int deviceType; unsigned int identifier; unsigned int attributes; unsigned int updateAttributes; unsigned int blockSizeOrOffset; unsigned int blockCount; unsigned int fileOffset; // Obsolete unsigned int fileSize; // Obsolete char partitionName[kPartitionNameMaxLength]; char flashFilename[kFlashFilenameMaxLength]; // USB flash filename char fotaFilename[kFotaFilenameMaxLength]; // Firmware over the air public: PitEntry(); ~PitEntry(); bool Matches(const PitEntry *otherPitEntry) const; bool IsFlashable(void) const { return strlen(partitionName) != 0; } unsigned int GetBinaryType(void) const { return binaryType; } void SetBinaryType(unsigned int binaryType) { this->binaryType = binaryType; } unsigned int GetDeviceType(void) const { return deviceType; } void SetDeviceType(unsigned int deviceType) { this->deviceType = deviceType; } unsigned int GetIdentifier(void) const { return identifier; } void SetIdentifier(unsigned int identifier) { this->identifier = identifier; } unsigned int GetAttributes(void) const { return attributes; } void SetAttributes(unsigned int attributes) { this->attributes = attributes; } unsigned int GetUpdateAttributes(void) const { return updateAttributes; } void SetUpdateAttributes(unsigned int updateAttributes) { this->updateAttributes = updateAttributes; } // Different versions of Loke (secondary bootloaders) on different devices intepret this differently. unsigned int GetBlockSizeOrOffset(void) const { return blockSizeOrOffset; } void SetBlockSizeOrOffset(unsigned int blockSizeOrOffset) { this->blockSizeOrOffset = blockSizeOrOffset; } unsigned int GetBlockCount(void) const { return blockCount; } void SetBlockCount(unsigned int blockCount) { this->blockCount = blockCount; } unsigned int GetFileOffset(void) const { return fileOffset; } void SetFileOffset(unsigned int fileOffset) { this->fileOffset = fileOffset; } unsigned int GetFileSize(void) const { return fileSize; } void SetFileSize(unsigned int fileSize) { this->fileSize = fileSize; } const char *GetPartitionName(void) const { return partitionName; } void SetPartitionName(const char *partitionName) { // This isn't strictly necessary but ensures no junk is left in our PIT file. memset(this->partitionName, 0, kPartitionNameMaxLength); if (strlen(partitionName) < kPartitionNameMaxLength) strcpy(this->partitionName, partitionName); else memcpy(this->partitionName, partitionName, kPartitionNameMaxLength - 1); } const char *GetFlashFilename(void) const { return flashFilename; } void SetFlashFilename(const char *flashFilename) { // This isn't strictly necessary but ensures no junk is left in our PIT file. memset(this->flashFilename, 0, kFlashFilenameMaxLength); if (strlen(partitionName) < kFlashFilenameMaxLength) strcpy(this->flashFilename, flashFilename); else memcpy(this->flashFilename, flashFilename, kFlashFilenameMaxLength - 1); } const char *GetFotaFilename(void) const { return fotaFilename; } void SetFotaFilename(const char *fotaFilename) { // This isn't strictly necessary but ensures no junk is left in our PIT file. memset(this->fotaFilename, 0, kFotaFilenameMaxLength); if (strlen(partitionName) < kFotaFilenameMaxLength) strcpy(this->fotaFilename, fotaFilename); else memcpy(this->fotaFilename, fotaFilename, kFotaFilenameMaxLength - 1); } }; class PitData { public: enum { kFileIdentifier = 0x12349876, kHeaderDataSize = 28, kPaddedSizeMultiplicand = 4096 }; private: unsigned int entryCount; // 0x04 char com_tar2[8+1]; // 0x08 char cpu_bl_id[8+1]; // 0x10 unsigned short luCount; // 0x18 // Entries start at 0x1C std::vector entries; static int UnpackInteger(const unsigned char *data, unsigned int offset) { #ifdef WORDS_BIGENDIAN int value = (data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3]; #else // Flip endianness int value = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24); #endif return (value); } static int UnpackShort(const unsigned char *data, unsigned int offset) { #ifdef WORDS_BIGENDIAN short value = (data[offset] << 8) | data[offset + 1]; #else // Flip endianness short value = data[offset] | (data[offset + 1] << 8); #endif return (value); } static void PackInteger(unsigned char *data, unsigned int offset, unsigned int value) { #ifdef WORDS_BIGENDIAN data[offset] = (value & 0xFF000000) >> 24; data[offset + 1] = (value & 0x00FF0000) >> 16; data[offset + 2] = (value & 0x0000FF00) >> 8; data[offset + 3] = value & 0x000000FF; #else // Flip endianness data[offset] = value & 0x000000FF; data[offset + 1] = (value & 0x0000FF00) >> 8; data[offset + 2] = (value & 0x00FF0000) >> 16; data[offset + 3] = (value & 0xFF000000) >> 24; #endif } static void PackShort(unsigned char *data, unsigned int offset, unsigned short value) { #ifdef WORDS_BIGENDIAN data[offset] = (value & 0xFF00) >> 8; data[offset + 1] = value & 0x00FF; #else // Flip endianness data[offset] = value & 0x00FF; data[offset + 1] = (value & 0xFF00) >> 8; #endif } public: PitData(); ~PitData(); bool Unpack(const unsigned char *data); void Pack(unsigned char *data) const; bool Matches(const PitData *otherPitData) const; void Clear(void); PitEntry *GetEntry(unsigned int index); const PitEntry *GetEntry(unsigned int index) const; PitEntry *FindEntry(const char *partitionName); const PitEntry *FindEntry(const char *partitionName) const; PitEntry *FindEntry(unsigned int partitionIdentifier); const PitEntry *FindEntry(unsigned int partitionIdentifier) const; unsigned int GetEntryCount(void) const { return entryCount; } unsigned int GetDataSize(void) const { return PitData::kHeaderDataSize + entryCount * PitEntry::kDataSize; } unsigned int GetPaddedSize(void) const { unsigned int dataSize = GetDataSize(); unsigned int paddedSize = (dataSize / kPaddedSizeMultiplicand) * kPaddedSizeMultiplicand; if (dataSize % kPaddedSizeMultiplicand != 0) paddedSize += kPaddedSizeMultiplicand; return paddedSize; } const char * GetComTar2(void) const { return com_tar2; } const char * GetCpuBlId(void) const { return cpu_bl_id; } unsigned int GetLUCount(void) const { return luCount; } }; } #endif