├── cmake └── version.txt ├── .gitattributes ├── img ├── main.png └── dialog.png ├── testdata ├── file_A_64b.bin ├── file_B_64b.bin ├── file_C_64b.bin ├── file_D_64b.bin ├── file_E_64b.bin ├── file_F_64b.bin ├── file_A_1k.bin ├── file_B_1k.bin ├── file_C_1k.bin ├── file_D_1k.bin ├── file_E_1k.bin ├── file_F_1k.bin ├── file_A_8k.bin ├── file_B_8k.bin ├── file_C_8k.bin ├── file_D_8k.bin ├── file_E_8k.bin └── file_F_8k.bin ├── resources ├── appicon.png └── icons.qrc ├── thirdparty ├── libusb-1.0.29.tar.bz2 ├── minipro-0.7.4.tar.bz2 ├── README_qt.md ├── README_minipro.md ├── README_libusb.md ├── LICENSE_qt6 └── LICENSE_libusb ├── src ├── SegmentTableView.h ├── main.cpp ├── LoadPreviewBar.h ├── HexView.h ├── SegmentView.h ├── SegmentTableView.cpp ├── MainWindow.h ├── ProcessHandling.h ├── HexView.cpp ├── SegmentView.cpp ├── LoadPreviewBar.cpp └── ProcessHandling.cpp ├── LICENSE ├── RELNOTES.md ├── .github └── workflows │ ├── macos.yml │ ├── macos12.yml │ ├── macos_universal.yml │ └── appimage.yml ├── README.md └── CMakeLists.txt /cmake/version.txt: -------------------------------------------------------------------------------- 1 | v0.1.0-4-g470ca73 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | cmake/version.txt export-subst 2 | 3 | -------------------------------------------------------------------------------- /img/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jartza/fireminipro/HEAD/img/main.png -------------------------------------------------------------------------------- /img/dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jartza/fireminipro/HEAD/img/dialog.png -------------------------------------------------------------------------------- /testdata/file_A_64b.bin: -------------------------------------------------------------------------------- 1 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -------------------------------------------------------------------------------- /testdata/file_B_64b.bin: -------------------------------------------------------------------------------- 1 | BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -------------------------------------------------------------------------------- /testdata/file_C_64b.bin: -------------------------------------------------------------------------------- 1 | CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC -------------------------------------------------------------------------------- /testdata/file_D_64b.bin: -------------------------------------------------------------------------------- 1 | DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD -------------------------------------------------------------------------------- /testdata/file_E_64b.bin: -------------------------------------------------------------------------------- 1 | EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE -------------------------------------------------------------------------------- /testdata/file_F_64b.bin: -------------------------------------------------------------------------------- 1 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -------------------------------------------------------------------------------- /resources/appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jartza/fireminipro/HEAD/resources/appicon.png -------------------------------------------------------------------------------- /thirdparty/libusb-1.0.29.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jartza/fireminipro/HEAD/thirdparty/libusb-1.0.29.tar.bz2 -------------------------------------------------------------------------------- /thirdparty/minipro-0.7.4.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jartza/fireminipro/HEAD/thirdparty/minipro-0.7.4.tar.bz2 -------------------------------------------------------------------------------- /resources/icons.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | appicon.png 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/SegmentTableView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class SegmentTableView : public QTableView { 7 | Q_OBJECT 8 | public: 9 | explicit SegmentTableView(QWidget *parent = nullptr); 10 | 11 | signals: 12 | void externalFilesDropped(int row, const QList &urls); 13 | 14 | protected: 15 | void dragEnterEvent(QDragEnterEvent *event) override; 16 | void dragMoveEvent(QDragMoveEvent *event) override; 17 | void dropEvent(QDropEvent *event) override; 18 | 19 | private: 20 | static constexpr const char *kSegmentMime = "application/x-fireminipro-segment-row"; 21 | bool isInternalDrag(const QMimeData *mime) const; 22 | }; 23 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "MainWindow.h" 18 | 19 | int main(int argc, char *argv[]) { 20 | QApplication app(argc, argv); 21 | QCoreApplication::setOrganizationName("Firebay refurb"); 22 | QCoreApplication::setApplicationName("fireminipro"); 23 | app.setWindowIcon(QIcon(":/appicon.png")); 24 | 25 | MainWindow w; 26 | w.show(); 27 | return app.exec(); 28 | } 29 | -------------------------------------------------------------------------------- /src/LoadPreviewBar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // Visualizes how a file will be merged into the current buffer. 8 | class LoadPreviewBar : public QWidget { 9 | public: 10 | explicit LoadPreviewBar(QWidget *parent = nullptr); 11 | 12 | void setParams(qulonglong bufSize, qulonglong off, qulonglong dataLen, qulonglong padLen); 13 | void setBufferSegments(QVector> segments); 14 | 15 | protected: 16 | QSize sizeHint() const override; 17 | void paintEvent(QPaintEvent *event) override; 18 | 19 | private: 20 | qulonglong bufSize_{}; 21 | qulonglong off_{}; 22 | qulonglong dataLen_{}; 23 | qulonglong padLen_{}; 24 | QVector> bufferSegments_; 25 | }; 26 | -------------------------------------------------------------------------------- /testdata/file_A_1k.bin: -------------------------------------------------------------------------------- 1 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -------------------------------------------------------------------------------- /testdata/file_B_1k.bin: -------------------------------------------------------------------------------- 1 | BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -------------------------------------------------------------------------------- /testdata/file_C_1k.bin: -------------------------------------------------------------------------------- 1 | CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC -------------------------------------------------------------------------------- /testdata/file_D_1k.bin: -------------------------------------------------------------------------------- 1 | DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD -------------------------------------------------------------------------------- /testdata/file_E_1k.bin: -------------------------------------------------------------------------------- 1 | EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE -------------------------------------------------------------------------------- /testdata/file_F_1k.bin: -------------------------------------------------------------------------------- 1 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -------------------------------------------------------------------------------- /thirdparty/README_qt.md: -------------------------------------------------------------------------------- 1 | # Qt 6 licensing notes 2 | 3 | `fireminipro` links dynamically against the following Qt 6 modules: 4 | 5 | - Qt6::Core 6 | - Qt6::Gui 7 | - Qt6::Widgets 8 | - Qt6::DBus 9 | 10 | The modules above fall under the GNU Lesser General Public License v3 (LGPLv3). FireMinipro 11 | fulfils the relevant terms as follows: 12 | 13 | - The DMG/AppImage bundles already ship the LGPLv3 text in `Resources/thirdparty/` 14 | (see [`LICENSE_qt6`](LICENSE_qt6)). 15 | - Release notes and packaging metadata record the exact Qt version used. Users can 16 | fetch the matching sources from https://download.qt.io/official_releases/. If a 17 | future release needs patched Qt sources, those patches must be mirrored here as well. 18 | - All builds link Qt dynamically (frameworks on macOS, `.so` on Linux), so users are 19 | free to replace the Qt libraries with their own builds. No anti-tamper measures are 20 | present. 21 | 22 | If the project ever adds a Qt module distributed solely under GPL terms, or switches to 23 | static linking, the licensing situation will need to be reassessed. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Jari Tulilahti / Firebay refurb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/HexView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class HexView : public QAbstractTableModel { 8 | Q_OBJECT 9 | public: 10 | explicit HexView(QObject *parent = nullptr); 11 | 12 | void setBufferRef(QByteArray *buffer); 13 | void clear(); 14 | 15 | void setBytesPerRow(int n); 16 | int getBytesPerRow() const { return bytesPerRow_; } 17 | 18 | void setSwapAscii16(bool on); 19 | 20 | // QAbstractTableModel 21 | int rowCount(const QModelIndex &parent = QModelIndex()) const override; 22 | int columnCount(const QModelIndex &parent = QModelIndex()) const override; 23 | QVariant headerData(int section, Qt::Orientation o, int role) const override; 24 | QVariant data(const QModelIndex &idx, int role) const override; 25 | Qt::ItemFlags flags(const QModelIndex &idx) const override; 26 | bool setData(const QModelIndex &idx, const QVariant &val, int role) override; 27 | 28 | // dirty tracking 29 | void clearDirty(); 30 | bool isDirty(qint64 off) const; 31 | int dirtyCount() const; 32 | 33 | private: 34 | static bool isPrintable(uint8_t b); 35 | 36 | QByteArray *buffer_{}; // not owned 37 | int bytesPerRow_{16}; 38 | bool swapAscii16_{false}; 39 | QSet dirty_; 40 | }; 41 | -------------------------------------------------------------------------------- /thirdparty/README_minipro.md: -------------------------------------------------------------------------------- 1 | # minipro CLI (bundled with FireMinipro) 2 | 3 | - **Version:** 0.7.4 4 | - **Commit:** 3088aecb6adac99b6a919b0382e01b2db7a718 5 | - **Upstream:** 6 | - **License:** GPLv3 (see `LICENSE_minipro`) 7 | 8 | ## Source archive 9 | 10 | Source code has been made available at: 11 | 12 | ``` 13 | https://github.com/Jartza/fireminipro/raw/refs/heads/main/thirdparty/minipro-0.7.4.tar.bz2 14 | ``` 15 | 16 | This tarball is kept in the repository to satisfy the GPL requirement to 17 | distribute the corresponding source alongside the binary. 18 | 19 | ## Rebuild instructions (macOS 12) 20 | 21 | 1. Extract the tarball, e.g. 22 | `tar xf thirdparty/minipro-0.7.4.tar.bz2`. 23 | 2. Build the bundled copy of libusb (see `README_libusb.md`) first, so the 24 | correct headers and dylib are available. 25 | 3. From the extracted `minipro` source directory: 26 | 27 | ```bash 28 | export PKG_CONFIG_PATH="$LIBUSB_STAGING/lib/pkgconfig" 29 | export CFLAGS="$CFLAGS -I$LIBUSB_STAGING/include" 30 | export LDFLAGS="$LDFLAGS -L$LIBUSB_STAGING/lib" 31 | make 32 | ``` 33 | 34 | 4. Copy the resulting `minipro` binary to `Contents/MacOS/`. 35 | 5. Copy `logicic.xml`, `infoic.xml`, and other data files to 36 | `Contents/Resources/minipro/`. 37 | 6. Include `LICENSE_minipro` and this README inside 38 | `Contents/Resources/thirdparty/minipro/`. 39 | 40 | These steps reproduce the bundled CLI that ships with FireMinipro (AppImage and DMG) 41 | -------------------------------------------------------------------------------- /thirdparty/README_libusb.md: -------------------------------------------------------------------------------- 1 | # libusb (bundled dependency) 2 | 3 | - **Version:** 1.0.29 4 | - **Upstream:** 5 | - **License:** LGPL-2.1 (see `LICENSE_libusb`) 6 | 7 | ## Source archive 8 | 9 | Source code has been made available at: 10 | 11 | ``` 12 | https://github.com/Jartza/fireminipro/raw/refs/heads/main/thirdparty/libusb-1.0.29.tar.bz2 13 | ``` 14 | 15 | Keeping this tarball in the repository ensures the corresponding source is 16 | available while distributing the binary, as required by the LGPL. 17 | 18 | ## Rebuild instructions (macOS 12) 19 | 20 | 1. Extract the tarball, e.g. 21 | `tar xf thirdparty/libusb-1.0.29.tar.bz2`. 22 | 2. Configure with a staging prefix to avoid system installation: 23 | 24 | ```bash 25 | ./configure --prefix="$PWD/install" 26 | make 27 | make install 28 | ``` 29 | 30 | 3. Copy the resulting library to the app bundle: 31 | 32 | ```bash 33 | cp install/lib/libusb-1.0.0.dylib \ 34 | fireminipro.app/Contents/Frameworks/ 35 | ``` 36 | 37 | 4. Update the library ID and the `minipro` binary so the loader finds the 38 | bundled dylib: 39 | 40 | ```bash 41 | install_name_tool -id \ 42 | @executable_path/../Frameworks/libusb-1.0.0.dylib \ 43 | fireminipro.app/Contents/Frameworks/libusb-1.0.0.dylib 44 | 45 | install_name_tool -change /usr/local/lib/libusb-1.0.0.dylib \ 46 | @executable_path/../Frameworks/libusb-1.0.0.dylib \ 47 | fireminipro.app/Contents/MacOS/minipro 48 | ``` 49 | 50 | 5. Include `LICENSE_libusb` and this README inside 51 | `Contents/Resources/thirdparty/libusb/`. 52 | 53 | These steps reproduce the dylib that ships with FireMinipro (AppImage and DMG). 54 | -------------------------------------------------------------------------------- /RELNOTES.md: -------------------------------------------------------------------------------- 1 | ## FireMinipro Release Notes 2 | 3 | Find releases from [here](https://github.com/Jartza/fireminipro/releases/) 4 | 5 | **Packages provided** 6 | - `fireminipro_.dmg` – MacOs 12 and later, universal binary (Apple Silicon and Intel) 7 | - `FireMinipro--x86_64.AppImage` – Linux AppImage (x86_64) 8 | 9 | All builds bundle the open-source **minipro** CLI and **libusb**, along with minipro's data files (`logicic.xml`, `infoic.xml`). Licence notices and the corresponding source archives live under `Contents/Resources/thirdparty/` (and in the repository’s `thirdparty/` folder) to satisfy GPLv3/LGPL requirements. 10 | 11 | --- 12 | 13 | ### Linux 14 | 15 | Download the AppImage, then: (replace \ with real file version number) 16 | 17 | ```bash 18 | chmod +x FireMinipro--x86_64.AppImage 19 | ./FireMinipro--x86_64.AppImage 20 | ``` 21 | 22 | The AppImage is fully self-contained; it should run on a clean Ubuntu/Debian install with no additional packages. 23 | 24 | --- 25 | 26 | ### macOS (Intel & Apple Silicon) 27 | 28 | Because the DMG is ad-hoc signed, macOS will block the first launch. Allow it via: 29 | 30 | - Open the `.dmg` and drag `fireminipro.app` into **Applications**. 31 | 32 | Two options to make it work, either: 33 | 34 | 1. Launch it once from Applications—macOS shows a warning dialog. 35 | 2. Open **System Settings → Privacy & Security** (use the link in the dialog’s `?` help). 36 | 3. Scroll down to “fireminipro.app was blocked…” and click **Open Anyway**. 37 | 4. Confirm in the new dialog (also **Open Anyway**) and authenticate. 38 | 5. Future launches will succeed normally. 39 | 40 | Of if you are more at home with Terminal / command-line: 41 | 42 | `sudo xattr -dr com.apple.quarantine /Applications/fireminipro.app` 43 | 44 | No additional Homebrew prerequisites are required: the DMG already contains `minipro`, `libusb`, and the Minipro XML data. 45 | 46 | --- 47 | -------------------------------------------------------------------------------- /src/SegmentView.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class SegmentView : public QAbstractTableModel { 7 | Q_OBJECT 8 | public: 9 | struct Segment { 10 | qulonglong start{}; 11 | qulonglong length{}; 12 | QString label; 13 | QString note; 14 | qulonglong id{}; 15 | }; 16 | 17 | explicit SegmentView(QObject *parent = nullptr); 18 | 19 | int rowCount(const QModelIndex &parent = QModelIndex()) const override; 20 | int columnCount(const QModelIndex &parent = QModelIndex()) const override; 21 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 22 | QVariant headerData(int section, Qt::Orientation orientation, 23 | int role = Qt::DisplayRole) const override; 24 | Qt::ItemFlags flags(const QModelIndex &index) const override; 25 | QStringList mimeTypes() const override; 26 | QMimeData *mimeData(const QModelIndexList &indexes) const override; 27 | bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, 28 | const QModelIndex &parent) override; 29 | Qt::DropActions supportedDropActions() const override; 30 | Qt::DropActions supportedDragActions() const override; 31 | bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, 32 | const QModelIndex &destinationParent, int destinationRow) override; 33 | 34 | void setSegments(QVector segments); 35 | void clear(); 36 | QVector segments() const; 37 | 38 | signals: 39 | void rowReordered(int from, int to); 40 | 41 | private: 42 | QVector rows_; 43 | 44 | static QString formatStart(qulonglong value); 45 | static QString formatEnd(qulonglong start, qulonglong length); 46 | static QString formatSize(qulonglong length); 47 | static QString formatLabel(const Segment &segment); 48 | }; 49 | -------------------------------------------------------------------------------- /src/SegmentTableView.cpp: -------------------------------------------------------------------------------- 1 | #include "SegmentTableView.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | SegmentTableView::SegmentTableView(QWidget *parent) 10 | : QTableView(parent) { 11 | setAcceptDrops(true); 12 | } 13 | 14 | void SegmentTableView::dragEnterEvent(QDragEnterEvent *event) { 15 | if (isInternalDrag(event->mimeData())) { 16 | QTableView::dragEnterEvent(event); 17 | return; 18 | } 19 | 20 | if (event->mimeData() && event->mimeData()->hasUrls()) { 21 | event->acceptProposedAction(); 22 | return; 23 | } 24 | 25 | QTableView::dragEnterEvent(event); 26 | } 27 | 28 | void SegmentTableView::dragMoveEvent(QDragMoveEvent *event) { 29 | if (isInternalDrag(event->mimeData())) { 30 | QTableView::dragMoveEvent(event); 31 | return; 32 | } 33 | 34 | if (event->mimeData() && event->mimeData()->hasUrls()) { 35 | event->acceptProposedAction(); 36 | return; 37 | } 38 | 39 | QTableView::dragMoveEvent(event); 40 | } 41 | 42 | void SegmentTableView::dropEvent(QDropEvent *event) { 43 | if (isInternalDrag(event->mimeData())) { 44 | QTableView::dropEvent(event); 45 | return; 46 | } 47 | 48 | if (!event->mimeData() || !event->mimeData()->hasUrls()) { 49 | QTableView::dropEvent(event); 50 | return; 51 | } 52 | 53 | const auto urls = event->mimeData()->urls(); 54 | if (urls.isEmpty()) { 55 | event->ignore(); 56 | return; 57 | } 58 | 59 | int targetRow = model() ? model()->rowCount() : 0; 60 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 61 | const QPoint pos = event->position().toPoint(); 62 | #else 63 | const QPoint pos = event->pos(); 64 | #endif 65 | const QModelIndex idx = indexAt(pos); 66 | if (idx.isValid()) { 67 | const QRect rect = visualRect(idx); 68 | if (pos.y() < rect.center().y()) 69 | targetRow = idx.row(); 70 | else 71 | targetRow = idx.row() + 1; 72 | } 73 | 74 | emit externalFilesDropped(targetRow, urls); 75 | event->acceptProposedAction(); 76 | } 77 | 78 | bool SegmentTableView::isInternalDrag(const QMimeData *mime) const { 79 | return mime && mime->hasFormat(kSegmentMime); 80 | } 81 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macOS DMG Apple Silicon 2 | on: 3 | # push: 4 | # tags: ["v*"] 5 | workflow_dispatch: 6 | 7 | jobs: 8 | build: 9 | runs-on: Firebay-MacOS12 10 | env: 11 | MACOSX_DEPLOYMENT_TARGET: "12.0" 12 | QT_HOME: "/Users/firebay/Qt/6.9.3/macos" 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | fetch-tags: true 18 | 19 | - name: Determine build version 20 | id: get_version 21 | run: | 22 | VERSION=$(git describe --tags --always --match "v[0-9]*" || echo "${GITHUB_REF_NAME}") 23 | VERSION="${VERSION#v}" 24 | VERSION="${VERSION%%-g*}" 25 | echo "version=$VERSION" >> $GITHUB_OUTPUT 26 | 27 | - name: Configure & Build 28 | run: | 29 | export PATH="$QT_HOME/bin:$PATH" 30 | cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_PREFIX_PATH="$QT_HOME" -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 31 | ninja -C build 32 | 33 | - name: Bundle minipro (arm64) 34 | run: | 35 | DIST_DIR="/Users/firebay/github-runner/3rd_party/minipro-dist-arm64" 36 | mkdir -p build/fireminipro.app/Contents/MacOS 37 | mkdir -p build/fireminipro.app/Contents/Frameworks 38 | mkdir -p build/fireminipro.app/Contents/Resources 39 | rsync -a "$DIST_DIR/MacOS/" build/fireminipro.app/Contents/MacOS/ 40 | rsync -a "$DIST_DIR/Frameworks/" build/fireminipro.app/Contents/Frameworks/ 41 | rsync -a "$DIST_DIR/Resources/" build/fireminipro.app/Contents/Resources/ 42 | 43 | - name: Deploy Qt into .app bundle 44 | run: | 45 | export PATH="$QT_HOME/bin:$PATH" 46 | "$QT_HOME/bin/macdeployqt" build/fireminipro.app -verbose=2 47 | 48 | - name: Ad-hoc sign app bundle 49 | run: | 50 | xattr -cr build/fireminipro.app 51 | codesign --force --deep --sign - build/fireminipro.app 52 | 53 | - name: Create DMG 54 | run: | 55 | rm -rf dist 56 | cp -av /Users/firebay/github-runner/3rd_party/dist dist 57 | cp -R build/fireminipro.app dist/ 58 | hdiutil create -volname FireMinipro -srcfolder dist -ov -format UDZO "build/fireminipro_${{ steps.get_version.outputs.version }}_arm64.dmg" 59 | 60 | - name: Set release tag name 61 | id: set_tag 62 | run: | 63 | if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then 64 | TAG_NAME="${GITHUB_REF_NAME}" 65 | if ! git rev-parse "$TAG_NAME" >/dev/null 2>&1; then 66 | git tag "$TAG_NAME" 67 | git push origin "$TAG_NAME" 68 | fi 69 | else 70 | TAG_NAME="${GITHUB_REF_NAME}" 71 | fi 72 | echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT 73 | 74 | - name: Upload release asset 75 | uses: softprops/action-gh-release@v2 76 | with: 77 | files: build/fireminipro_*_arm64.dmg 78 | tag_name: ${{ steps.set_tag.outputs.tag_name }} 79 | env: 80 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 81 | -------------------------------------------------------------------------------- /.github/workflows/macos12.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/macos12.yml 2 | name: macOS DMG intel 3 | on: 4 | # push: 5 | # tags: ["v*"] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: Firebay-MacOS12 11 | env: 12 | MACOSX_DEPLOYMENT_TARGET: "12.0" 13 | QT_HOME: "/Users/firebay/Qt/6.9.3/macos" 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | fetch-tags: true 19 | 20 | - name: Determine build version 21 | id: get_version 22 | run: | 23 | VERSION=$(git describe --tags --always --match "v[0-9]*" || echo "${GITHUB_REF_NAME}") 24 | VERSION="${VERSION#v}" 25 | VERSION="${VERSION%%-g*}" 26 | echo "version=$VERSION" >> $GITHUB_OUTPUT 27 | 28 | - name: Configure & Build (x86_64) 29 | run: | 30 | export PATH="$QT_HOME/bin:$PATH" 31 | arch -x86_64 cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_PREFIX_PATH="$QT_HOME" -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 32 | arch -x86_64 ninja -C build 33 | 34 | - name: Bundle minipro (x86_64) 35 | run: | 36 | DIST_DIR="/Users/firebay/github-runner/3rd_party/minipro-dist-x86_64" 37 | mkdir -p build/fireminipro.app/Contents/MacOS 38 | mkdir -p build/fireminipro.app/Contents/Frameworks 39 | mkdir -p build/fireminipro.app/Contents/Resources 40 | rsync -a "$DIST_DIR/MacOS/" build/fireminipro.app/Contents/MacOS/ 41 | rsync -a "$DIST_DIR/Frameworks/" build/fireminipro.app/Contents/Frameworks/ 42 | rsync -a "$DIST_DIR/Resources/" build/fireminipro.app/Contents/Resources/ 43 | 44 | - name: Deploy Qt into .app bundle 45 | run: | 46 | export PATH="$QT_HOME/bin:$PATH" 47 | arch -x86_64 "$QT_HOME/bin/macdeployqt" build/fireminipro.app -verbose=2 48 | 49 | - name: Ad-hoc sign app bundle 50 | run: | 51 | arch -x86_64 xattr -cr build/fireminipro.app 52 | arch -x86_64 codesign --force --deep --sign - build/fireminipro.app 53 | 54 | - name: Create DMG 55 | run: | 56 | rm -rf dist 57 | cp -av /Users/firebay/github-runner/3rd_party/dist dist 58 | cp -R build/fireminipro.app dist/ 59 | arch -x86_64 hdiutil create -volname FireMinipro -srcfolder dist -ov -format UDZO "build/fireminipro_${{ steps.get_version.outputs.version }}_x86_64.dmg" 60 | 61 | - name: Set release tag name 62 | id: set_tag 63 | run: | 64 | if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then 65 | TAG_NAME="${GITHUB_REF_NAME}" 66 | if ! git rev-parse "$TAG_NAME" >/dev/null 2>&1; then 67 | git tag "$TAG_NAME" 68 | git push origin "$TAG_NAME" 69 | fi 70 | else 71 | TAG_NAME="${GITHUB_REF_NAME}" 72 | fi 73 | echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT 74 | 75 | - name: Upload release asset 76 | uses: softprops/action-gh-release@v2 77 | with: 78 | files: build/fireminipro_*_x86_64.dmg 79 | tag_name: ${{ steps.set_tag.outputs.tag_name }} 80 | env: 81 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 82 | -------------------------------------------------------------------------------- /.github/workflows/macos_universal.yml: -------------------------------------------------------------------------------- 1 | name: macOS DMG Universal 2 | on: 3 | push: 4 | tags: ["v*"] 5 | workflow_dispatch: 6 | 7 | jobs: 8 | build: 9 | runs-on: Firebay-MacOS12 10 | env: 11 | MACOSX_DEPLOYMENT_TARGET: "12.0" 12 | QT_HOME: "/Users/firebay/Qt/6.9.3/macos" 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | fetch-tags: true 18 | 19 | - name: Determine build version 20 | id: get_version 21 | run: | 22 | VERSION=$(git describe --tags --always --match "v[0-9]*" || echo "${GITHUB_REF_NAME}") 23 | VERSION="${VERSION#v}" 24 | VERSION="${VERSION%%-g*}" 25 | echo "version=$VERSION" >> $GITHUB_OUTPUT 26 | 27 | - name: Configure & Build (Universal) 28 | run: | 29 | export PATH="$QT_HOME/bin:$PATH" 30 | cmake -B build -G Ninja \ 31 | -DCMAKE_BUILD_TYPE=Release \ 32 | -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ 33 | -DCMAKE_PREFIX_PATH="$QT_HOME" \ 34 | -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 35 | ninja -C build 36 | 37 | - name: Bundle minipro + libusb (universal) 38 | run: | 39 | DIST_DIR="/Users/firebay/github-runner/3rd_party/minipro-dist-universal" 40 | mkdir -p build/fireminipro.app/Contents/MacOS 41 | mkdir -p build/fireminipro.app/Contents/Frameworks 42 | mkdir -p build/fireminipro.app/Contents/Resources 43 | rsync -a "$DIST_DIR/MacOS/" build/fireminipro.app/Contents/MacOS/ 44 | rsync -a "$DIST_DIR/Frameworks/" build/fireminipro.app/Contents/Frameworks/ 45 | rsync -a "$DIST_DIR/Resources/" build/fireminipro.app/Contents/Resources/ 46 | 47 | - name: Deploy Qt into .app bundle 48 | run: | 49 | export PATH="$QT_HOME/bin:$PATH" 50 | "$QT_HOME/bin/macdeployqt" build/fireminipro.app -verbose=2 51 | 52 | - name: Ad-hoc sign app bundle 53 | run: | 54 | xattr -cr build/fireminipro.app 55 | codesign --force --deep --sign - build/fireminipro.app 56 | 57 | - name: Create DMG 58 | run: | 59 | rm -rf dist 60 | cp -av /Users/firebay/github-runner/3rd_party/dist dist 61 | cp -R build/fireminipro.app dist/ 62 | hdiutil create -volname FireMinipro -srcfolder dist -ov -format UDZO "build/fireminipro_${{ steps.get_version.outputs.version }}.dmg" 63 | 64 | - name: Set release tag name 65 | id: set_tag 66 | run: | 67 | if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then 68 | TAG_NAME="${GITHUB_REF_NAME}" 69 | if ! git rev-parse "$TAG_NAME" >/dev/null 2>&1; then 70 | git tag "$TAG_NAME" 71 | git push origin "$TAG_NAME" 72 | fi 73 | else 74 | TAG_NAME="${GITHUB_REF_NAME}" 75 | fi 76 | echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT 77 | 78 | - name: Upload release asset 79 | uses: softprops/action-gh-release@v2 80 | with: 81 | files: build/fireminipro_${{ steps.get_version.outputs.version }}.dmg 82 | tag_name: ${{ steps.set_tag.outputs.tag_name }} 83 | env: 84 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 85 | -------------------------------------------------------------------------------- /src/MainWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ProcessHandling.h" 9 | 10 | class QComboBox; 11 | class QPushButton; 12 | class QTableView; 13 | class QPlainTextEdit; 14 | class QCheckBox; 15 | class QLabel; 16 | class QWidget; 17 | class HexView; 18 | class QProgressBar; 19 | class SegmentView; 20 | class QModelIndex; 21 | class SegmentTableView; 22 | 23 | class MainWindow : public QMainWindow { 24 | Q_OBJECT 25 | public: 26 | explicit MainWindow(QWidget *parent = nullptr); 27 | ~MainWindow() override = default; 28 | 29 | private slots: 30 | void saveBufferToFile(); 31 | void loadAtOffsetDialog(QString path = {}, bool deleteOnFinish = false); 32 | void loadFileAppendDialog(); 33 | void onDevicesScanned(const QStringList &names); 34 | void onDevicesListed(const QStringList &names); 35 | void onSegmentRowReordered(int from, int to); 36 | void onLegendRowDoubleClicked(const QModelIndex &index); 37 | void onLegendFilesDropped(int row, const QList &urls); 38 | void onLegendContextMenuRequested(const QPoint &pos); 39 | 40 | QString pickFile(const QString &title, QFileDialog::AcceptMode mode, 41 | const QString &filters = QString()); 42 | 43 | private: 44 | // Target and device 45 | QComboBox *comboProgrammer{}; 46 | QComboBox *comboDevice{}; 47 | QPushButton *btnRescan{}; 48 | 49 | // Chip information 50 | QLabel *chipName{}; 51 | QLabel *chipPackage{}; 52 | QLabel *chipMemory{}; 53 | QLabel *chipBusWidth{}; 54 | QLabel *chipProtocol{}; 55 | QLabel *chipReadBuf{}; 56 | QLabel *chipWriteBuf{}; 57 | 58 | // Buffer group 59 | QPushButton *btnClear{}; 60 | QPushButton *btnLoadBinary{}; 61 | QPushButton *btnLoadAdvanced{}; 62 | QPushButton *btnSave{}; 63 | QPushButton *btnRead{}; 64 | QPushButton *btnWrite{}; 65 | QCheckBox *chkAsciiSwap{}; 66 | QLabel *lblBufSize{}; 67 | 68 | // Device operations 69 | QPushButton *btnBlankCheck{}; 70 | QPushButton *btnEraseDevice{}; 71 | QPushButton *btnTestLogic{}; 72 | 73 | // Device options 74 | QCheckBox *chkSkipVerify{}; 75 | QCheckBox *chkIgnoreId{}; 76 | QCheckBox *chkSkipId{}; 77 | QCheckBox *chkNoSizeErr{}; 78 | 79 | // Views 80 | QTableView *tableHex{}; 81 | QPlainTextEdit *log{}; 82 | QFont logFontDefault_; 83 | QFont logFontFixed_; 84 | 85 | // Hex view model 86 | HexView *hexModel{}; 87 | 88 | // Progress bar 89 | QProgressBar* progReadWrite{}; 90 | 91 | // In-memory buffer 92 | QByteArray buffer_; 93 | QString lastPath_; 94 | QString pendingWriteTempPath_; 95 | 96 | // Buffer segment legend storage 97 | struct BufferSegment { 98 | qulonglong start{}; 99 | qulonglong length{}; 100 | QString label; 101 | QString note; 102 | qulonglong id{}; 103 | }; 104 | 105 | // Buffer segment legend 106 | QList bufferSegments{}; 107 | SegmentTableView *legendTable{}; 108 | SegmentView *segmentModel{}; 109 | qulonglong nextSegmentId_ = 1; 110 | 111 | // Process handling helper 112 | ProcessHandling *proc{}; 113 | 114 | // If selected device is a logic IC 115 | bool currentIsLogic_ = false; 116 | 117 | // Buffer legend manipulation 118 | void updateLegendTable(); 119 | void addSegmentAndRefresh(qulonglong start, qulonglong length, const QString &label); 120 | void applyLogFontForDevice(); 121 | void deleteSegmentAt(int row); 122 | void fillSegmentWithValue(int row, quint8 value); 123 | 124 | // Helpers 125 | QStringList optionFlags() const; 126 | void setUiEnabled(bool on); 127 | void disableBusyButtons(); 128 | void updateActionEnabling(); 129 | void updateChipInfo(const ProcessHandling::ChipInfo &ci); 130 | void clearChipInfo(); 131 | QString exportBufferToTempFileLocal(const QString& baseName); 132 | 133 | // parsing / buffer helpers 134 | bool parseSizeLike(const QString &in, qulonglong &out); 135 | void ensureBufferSize(int newSize, char padByte); 136 | void patchBuffer(int offset, const QByteArray &data, char padByte); 137 | 138 | protected: 139 | bool eventFilter(QObject *obj, QEvent *event) override; 140 | 141 | }; 142 | -------------------------------------------------------------------------------- /src/ProcessHandling.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class ProcessHandling : public QObject { 7 | Q_OBJECT 8 | public: 9 | explicit ProcessHandling(QObject *parent = nullptr); 10 | void sendResponse(const QString &input); 11 | // Fire-and-forget scan for connected programmers (minipro -k) 12 | void scanConnectedDevices(); 13 | // Fetch supported devices for a given programmer (minipro -q -l) 14 | void fetchSupportedDevices(const QString &programmer); 15 | // Fetch information about selected chip (minipro -d "") 16 | void fetchChipInfo(const QString &programmer, const QString &device); 17 | // Read from chip into buffer (minipro -r ) 18 | void readChipImage(const QString& programmer, 19 | const QString& device, 20 | const QStringList& extraFlags = {}); 21 | void writeChipImage(const QString& programmer, 22 | const QString& device, 23 | const QString& filePath, 24 | const QStringList& extraFlags = {}); 25 | // Check if chip is blank (minipro -b) 26 | void checkIfBlank(const QString &programmer, 27 | const QString &device, 28 | const QStringList &extraFlags = {}); 29 | // Erase chip (minipro -e) 30 | void eraseChip(const QString &programmer, 31 | const QString &device, 32 | const QStringList &extraFlags = {}); 33 | // Test logic chip (minipro -T) 34 | void testLogicChip(const QString &programmer, 35 | const QString &device, 36 | const QStringList &extraFlags = {}); 37 | 38 | struct ChipInfo { 39 | QString baseName; // e.g. "AM2764A" (may be empty) 40 | QString package; // e.g. "DIP28" (may be empty) 41 | quint64 bytes = 0; // 0 if unknown 42 | int wordBits = 0; // 8 for byte-wide, 16 for 16-bit; 0 if unknown 43 | QString protocol; // "0x07" or empty 44 | int readBuf = 0; // bytes; 0 if unknown 45 | int writeBuf = 0; // bytes; 0 if unknown 46 | QString raw; // full captured text for debugging 47 | bool isLogic{}; // true if logic chip, false if eeprom/flash 48 | int vectorCount{}; // for logic chips, number of vectors 49 | }; 50 | 51 | signals: 52 | // Normal log output 53 | void logLine(const QString &text); 54 | // Error log output 55 | void errorLine(const QString &text); 56 | // Parsed progress % 57 | void progress(int percent, const QString& phase); 58 | // Emitted when a prompt is detected from the process 59 | void promptDetected(const QString &promptText); 60 | // Emitted after scanConnectedDevices() completes 61 | void devicesScanned(const QStringList &names); 62 | void devicesListed(const QStringList &names); 63 | // Emitted when chip info is fetched 64 | void chipInfoReady(const ChipInfo &ci); 65 | // Emitted when chip reading is successful 66 | void readReady(const QString& tempPath); 67 | // Emitted when chip writing is done 68 | void writeDone(); 69 | // Emitted when process starts 70 | void started(); 71 | // Emitted when process finishes 72 | void finished(int exitCode, QProcess::ExitStatus status); 73 | 74 | private slots: 75 | void handleStdout(); 76 | void handleFinished(int exitCode, QProcess::ExitStatus status); 77 | 78 | private: 79 | void processOutputLine(QString line); 80 | 81 | // Internal mode to disambiguate generic runs vs scans 82 | enum class Mode { 83 | Idle, 84 | Generic, 85 | Scan, 86 | DeviceList, 87 | ChipInfo, 88 | Reading, 89 | Writing, 90 | Logic, 91 | }; 92 | 93 | Mode mode_{Mode::Idle}; 94 | QString stdoutBuffer_; 95 | QString stdoutFragment_; 96 | QString pendingTempPath_; 97 | 98 | QString resolveMiniproPath(); 99 | void startMinipro(Mode mode, const QStringList& args); 100 | QStringList parseProgrammerList(const QString &text) const; 101 | ChipInfo parseChipInfo(const QString &text) const; 102 | static QString stripAnsi(QString s); 103 | static int extractPercent(const QString &line); 104 | static QString detectPhaseText(const QString &line); 105 | QProcess process_; 106 | }; 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FireMinipro 2 | 3 | **FireMinipro** is a modern, cross-platform graphical front-end for the [Minipro](https://gitlab.com/DavidGriffith/minipro) programmer software. 4 | It is designed to be fast, intuitive, and lightweight — with special emphasis on **buffer management** and **visual clarity**. 5 | 6 | Unlike traditional programmer utilities, FireMinipro lets you view, merge, and manipulate binary data buffers easily before writing to or after reading from an EPROM, EEPROM, or logic IC. 7 | It aims to provide a comfortable workflow for both hobbyists and professionals working with TL866, T48 and compatible programmers. 8 | 9 | --- 10 | 11 | ## Screenshots 12 | 13 | ![Main Window](img/main.png) 14 | ![Dialog Example](img/dialog.png) 15 | 16 | --- 17 | 18 | ## Features 19 | 20 | - **Buffer visualization:** Load multiple binary files at arbitrary offsets, visualize overlaps, and highlight padding. 21 | - **Flexible file operations:** Load, clear, merge, and save buffers of any size. 22 | - **Drag and Drop:** Files loaded to buffer can be reorganized by dragging, or you can drag files to the buffer from file explorer / finder. 23 | - **Device awareness:** Automatically detects connected Minipro programmers and supported device lists. 24 | - **Chip info:** Displays memory size, package and protocol details directly from Minipro. 25 | - **Device operations:** Blank check, erase, logic test, read, and write functions fully integrated. 26 | - **Progress tracking:** Live progress and operation status while reading or writing chips. 27 | - **Cross-platform support:** Works on both macOS and Linux using Qt6. 28 | 29 | --- 30 | 31 | ## Releases 32 | 33 | If you do not feel comfortable building software yourself, there are 34 | **AppImage for Linux** and **DMG for MacOs** (12 and up) provided in the [releases](https://github.com/Jartza/fireminipro/releases/) page. 35 | 36 | --- 37 | 38 | ## Building prerequisites 39 | 40 | ### macOS 41 | Install dependencies using [Homebrew](https://brew.sh/): 42 | 43 | ```bash 44 | brew install cmake ninja qt6 minipro 45 | ``` 46 | 47 | Either follow the build instruction below, or if you are brave, instead of above line, use the following: 48 | ```bash 49 | brew tap Jartza/fireminipro 50 | brew install --build-from-source fireminipro 51 | ``` 52 | After which you can start fireminipro from command-line. 53 | 54 | 55 | 56 | ### Linux (Ubuntu/Debian) 57 | You need Minipro and the development toolchain for Qt6 and CMake. 58 | Install required packages using: 59 | 60 | ```bash 61 | sudo apt update 62 | sudo apt install build-essential ninja-build cmake libgl1-mesa-dev qt6-base-dev libxkbcommon-dev 63 | ``` 64 | 65 | > **Note:** 66 | > The `minipro` package is not available in most distributions by default. 67 | > You can install it manually by cloning and building from source: 68 | > ```bash 69 | > git clone https://gitlab.com/DavidGriffith/minipro.git 70 | > cd minipro 71 | > make 72 | > sudo make install 73 | > ``` 74 | 75 | --- 76 | 77 | ## Building FireMinipro 78 | 79 | ### 1. Clone the repository 80 | ```bash 81 | git clone https://github.com/Jartza/fireminipro.git 82 | cd fireminipro 83 | ``` 84 | 85 | ### 2. Configure and build 86 | FireMinipro uses **CMake** and **Ninja** for fast, cross-platform builds. 87 | 88 | ```bash 89 | mkdir build 90 | cd build 91 | cmake -G Ninja .. 92 | ninja 93 | cd .. 94 | ``` 95 | 96 | ### 3. Run the application 97 | On macOS: 98 | ```bash 99 | ./build/fireminipro.app/Contents/MacOS/fireminipro 100 | ``` 101 | 102 | On Linux: 103 | ```bash 104 | ./build/fireminipro 105 | ``` 106 | 107 | --- 108 | 109 | ## Using FireMinipro 110 | 111 | 1. **Connect** your supported programmer (T48, TL866II+, etc.). 112 | 2. FireMinipro automatically detects connected devices at startup. 113 | 3. **Select** your target chip and use the built-in *Device Operations* to: 114 | - Read chip contents into the buffer. 115 | - Write buffer data to the chip. 116 | - Load files into buffer at freely definable offset 117 | - Perform read, write, erase, blank check, or logic IC test operations. 118 | 4. **Edit or combine** ROM/binary file data directly in the buffer before writing. 119 | 120 | --- 121 | 122 | ## Notes 123 | 124 | - Current release is still on its' early days, might not yet be complete or even usable for all 125 | purposes. Please report issues, send enhancement requests and tell about success-stories: 126 | GitHub repo has both Issues and Discussions -tabs ready for use. 127 | - FireMinipro uses `minipro` under the hood, so your connected programmer must be supported by Minipro. 128 | - On macOS, the app includes a custom icon and can be bundled as a `.app` package for easier launching. 129 | - On Linux, the icon will appear in most desktop environments after installation (when packaged later). 130 | 131 | --- 132 | 133 | ## TODO 134 | 135 | - Implement device `verify` option 136 | - Parsing the Logic IC test output, for now it's just printed to log 137 | - More tools (split .bin file into chunks, byte-split Amiga rom binaries etc...) 138 | - AVR/PIC/GAL programming has not been tested or verified and needs future enhancements. 139 | 140 | --- 141 | 142 | ## License 143 | 144 | This project is released under the MIT License. 145 | See [LICENSE](LICENSE) for details. 146 | 147 | ### Third-party components & Acknowledgements 148 | 149 | FireMinipro bundles a few external utilities inside the macOS and Linux packages. 150 | The corresponding source archives and licence texts live in [`thirdparty/`](thirdparty/): 151 | 152 | - **minipro** (GPLv3) by David Griffith – used to communicate with TL866/T48 programmers. 153 | - **libusb** (LGPL 2.1) – runtime dependency required by `minipro`. 154 | - **Qt 6** (LGPLv3) – cross-platform GUI framework used by FireMinipro. See [`thirdparty/README_qt.md`](thirdparty/README_qt.md) and the bundled [`thirdparty/LICENSE_qt6`](thirdparty/LICENSE_qt6). 155 | 156 | The DMG/AppImage builds copy the relevant licences into `Resources/thirdparty/` so users 157 | always have access to the attribution and source information. Details about the Qt 6 158 | modules and their LGPL requirements are documented in [`thirdparty/README_qt.md`](thirdparty/README_qt.md). 159 | -------------------------------------------------------------------------------- /src/HexView.cpp: -------------------------------------------------------------------------------- 1 | #include "HexView.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | HexView::HexView(QObject *parent) : QAbstractTableModel(parent) {} 9 | 10 | void HexView::setBufferRef(QByteArray *buffer) { 11 | beginResetModel(); 12 | buffer_ = buffer; 13 | dirty_.clear(); // reset dirty tracking when buffer changes 14 | endResetModel(); 15 | } 16 | 17 | void HexView::clear() { 18 | beginResetModel(); 19 | buffer_ = nullptr; 20 | dirty_.clear(); 21 | endResetModel(); 22 | } 23 | 24 | void HexView::setBytesPerRow(int n) { 25 | if (n < 1) n = 1; 26 | if (bytesPerRow_ == n) return; 27 | beginResetModel(); 28 | bytesPerRow_ = n; 29 | endResetModel(); 30 | } 31 | 32 | void HexView::setSwapAscii16(bool on) { 33 | if (swapAscii16_ == on) return; 34 | swapAscii16_ = on; 35 | if (rowCount() > 0 && columnCount() > 0) { 36 | emit headerDataChanged(Qt::Horizontal, 0, columnCount()-1); 37 | emit dataChanged(index(0, 0), index(rowCount()-1, columnCount()-1)); 38 | } 39 | } 40 | 41 | int HexView::rowCount(const QModelIndex &parent) const { 42 | if (parent.isValid() || !buffer_) return 0; 43 | const auto n = buffer_->size(); 44 | return (n + bytesPerRow_ - 1) / bytesPerRow_; 45 | } 46 | 47 | int HexView::columnCount(const QModelIndex &parent) const { 48 | if (parent.isValid()) return 0; 49 | // 0 = address, 1..bytesPerRow_ = hex bytes, last = ASCII 50 | return 1 + bytesPerRow_ + 1; 51 | } 52 | 53 | QVariant HexView::headerData(int section, Qt::Orientation o, int role) const { 54 | if (o != Qt::Horizontal || role != Qt::DisplayRole) return {}; 55 | if (section == 0) return QStringLiteral("Addr"); 56 | if (section == 1 + bytesPerRow_) { 57 | return swapAscii16_ ? QStringLiteral("ASCII (swapped)") : QStringLiteral("ASCII"); 58 | } 59 | if (section >= 1 && section <= bytesPerRow_) { 60 | return QString("%1").arg(section-1, 2, 16, QLatin1Char('0')).toUpper(); 61 | } 62 | return {}; 63 | } 64 | 65 | static inline bool bytePrintable(uint8_t b) { 66 | return b >= 32 && b <= 126; 67 | } 68 | 69 | QVariant HexView::data(const QModelIndex &idx, int role) const { 70 | if (!idx.isValid() || !buffer_) return {}; 71 | const int r = idx.row(); 72 | const int c = idx.column(); 73 | 74 | const qint64 rowBase = qint64(r) * bytesPerRow_; 75 | 76 | // Background tint for dirty bytes (hex columns) or for ascii row if any byte dirty 77 | if (role == Qt::BackgroundRole) { 78 | if (c >= 1 && c <= bytesPerRow_) { 79 | const qint64 off = rowBase + (c - 1); 80 | if (off < buffer_->size() && isDirty(off)) return QBrush(QColor(255,245,200)); 81 | } else if (c == 1 + bytesPerRow_) { 82 | for (int i=0; isize() && isDirty(off)) return QBrush(QColor(255,245,200)); 85 | } 86 | } 87 | } 88 | 89 | if (role == Qt::TextAlignmentRole) { 90 | if (c == 0) return int(Qt::AlignRight | Qt::AlignVCenter); 91 | if (c >= 1 && c <= bytesPerRow_) return int(Qt::AlignHCenter | Qt::AlignVCenter); 92 | return int(Qt::AlignLeft | Qt::AlignVCenter); 93 | } 94 | 95 | if (role == Qt::DisplayRole) { 96 | // address 97 | if (c == 0) { 98 | return QString("%1").arg(rowBase, 8, 16, QLatin1Char('0')).toUpper(); 99 | } 100 | 101 | // hex bytes 102 | if (c >= 1 && c <= bytesPerRow_) { 103 | const qint64 off = rowBase + (c - 1); 104 | if (off >= buffer_->size()) return QString(" "); 105 | const uint8_t b = uint8_t(buffer_->at(int(off))); 106 | return QString("%1").arg(b, 2, 16, QLatin1Char('0')).toUpper(); 107 | } 108 | 109 | // ascii column 110 | if (c == 1 + bytesPerRow_) { 111 | QString s; s.reserve(bytesPerRow_); 112 | for (int i=0; i= buffer_->size()) { s.append(' '); continue; } 115 | const uint8_t b = uint8_t(buffer_->at(int(off))); 116 | uint8_t ch = b; 117 | if (swapAscii16_) { 118 | // swap each pair within the row region 119 | const int iPair = (i ^ 1); 120 | const qint64 other = rowBase + iPair; 121 | if (other < buffer_->size()) ch = uint8_t(buffer_->at(int(other))); 122 | } 123 | s.append(bytePrintable(ch) ? QChar(ch) : QChar('.')); 124 | } 125 | return s; 126 | } 127 | } 128 | 129 | return {}; 130 | } 131 | 132 | Qt::ItemFlags HexView::flags(const QModelIndex &idx) const { 133 | if (!idx.isValid()) return Qt::NoItemFlags; 134 | const int c = idx.column(); 135 | // editable hex bytes only (not address or ascii) 136 | if (c >= 1 && c <= bytesPerRow_) return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; 137 | return Qt::ItemIsEnabled | Qt::ItemIsSelectable; 138 | } 139 | 140 | bool HexView::setData(const QModelIndex &idx, const QVariant &val, int role) { 141 | if (role != Qt::EditRole || !buffer_) return false; 142 | const int r = idx.row(); 143 | const int c = idx.column(); 144 | if (c < 1 || c > bytesPerRow_) return false; 145 | 146 | const qint64 off = qint64(r) * bytesPerRow_ + (c - 1); 147 | if (off < 0 || off >= buffer_->size()) return false; 148 | 149 | // parse two-hex-digit string 150 | bool ok = false; 151 | QString t = val.toString().trimmed(); 152 | if (t.startsWith("0x", Qt::CaseInsensitive)) t = t.mid(2); 153 | const int b = t.toInt(&ok, 16); 154 | if (!ok || b < 0 || b > 255) return false; 155 | 156 | char &ref = (*buffer_)[int(off)]; 157 | if (ref == char(b)) return false; 158 | ref = char(b); 159 | dirty_.insert(off); 160 | emit dataChanged(index(r, 0), index(r, columnCount()-1)); 161 | return true; 162 | } 163 | 164 | void HexView::clearDirty() { dirty_.clear(); } 165 | bool HexView::isDirty(qint64 off) const { return dirty_.contains(off); } 166 | int HexView::dirtyCount() const { return dirty_.size(); } 167 | -------------------------------------------------------------------------------- /src/SegmentView.cpp: -------------------------------------------------------------------------------- 1 | #include "SegmentView.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace { 10 | constexpr auto kMimeType = "application/x-fireminipro-segment-row"; 11 | } 12 | 13 | SegmentView::SegmentView(QObject *parent) 14 | : QAbstractTableModel(parent) {} 15 | 16 | int SegmentView::rowCount(const QModelIndex &parent) const { 17 | if (parent.isValid()) return 0; 18 | return rows_.size(); 19 | } 20 | 21 | int SegmentView::columnCount(const QModelIndex &parent) const { 22 | if (parent.isValid()) return 0; 23 | return 4; 24 | } 25 | 26 | QVariant SegmentView::data(const QModelIndex &index, int role) const { 27 | if (!index.isValid()) return {}; 28 | if (index.row() < 0 || index.row() >= rows_.size()) return {}; 29 | 30 | const auto &segment = rows_.at(index.row()); 31 | 32 | switch (role) { 33 | case Qt::DisplayRole: 34 | switch (index.column()) { 35 | case 0: return formatStart(segment.start); 36 | case 1: return formatEnd(segment.start, segment.length); 37 | case 2: return formatSize(segment.length); 38 | case 3: return formatLabel(segment); 39 | default: return {}; 40 | } 41 | case Qt::TextAlignmentRole: 42 | if (index.column() < 3) return int(Qt::AlignRight | Qt::AlignVCenter); 43 | return int(Qt::AlignLeft | Qt::AlignVCenter); 44 | default: 45 | return {}; 46 | } 47 | } 48 | 49 | QVariant SegmentView::headerData(int section, Qt::Orientation orientation, int role) const { 50 | if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { 51 | switch (section) { 52 | case 0: return tr("Start"); 53 | case 1: return tr("End"); 54 | case 2: return tr("Size"); 55 | case 3: return tr("File"); 56 | default: break; 57 | } 58 | } 59 | return QAbstractTableModel::headerData(section, orientation, role); 60 | } 61 | 62 | Qt::ItemFlags SegmentView::flags(const QModelIndex &index) const { 63 | auto f = QAbstractTableModel::flags(index); 64 | if (index.isValid()) 65 | f |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; 66 | else 67 | f |= Qt::ItemIsDropEnabled; 68 | return f; 69 | } 70 | 71 | QStringList SegmentView::mimeTypes() const { 72 | return {QString::fromLatin1(kMimeType)}; 73 | } 74 | 75 | QMimeData *SegmentView::mimeData(const QModelIndexList &indexes) const { 76 | if (indexes.isEmpty()) return nullptr; 77 | const int row = indexes.first().row(); 78 | if (row < 0 || row >= rows_.size()) return nullptr; 79 | 80 | auto *mime = new QMimeData; 81 | QByteArray encoded; 82 | QDataStream out(&encoded, QIODevice::WriteOnly); 83 | out << row; 84 | mime->setData(kMimeType, encoded); 85 | return mime; 86 | } 87 | 88 | bool SegmentView::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, 89 | const QModelIndex &parent) { 90 | Q_UNUSED(column); 91 | if (action == Qt::IgnoreAction) return true; 92 | if (!data || !data->hasFormat(kMimeType)) return false; 93 | 94 | QByteArray encoded = data->data(kMimeType); 95 | QDataStream stream(&encoded, QIODevice::ReadOnly); 96 | int sourceRow = -1; 97 | stream >> sourceRow; 98 | if (sourceRow < 0 || sourceRow >= rows_.size()) return false; 99 | 100 | int destinationRow = row; 101 | if (destinationRow == -1) { 102 | destinationRow = parent.isValid() ? parent.row() : rows_.size(); 103 | } 104 | 105 | if (destinationRow > sourceRow) { 106 | if (destinationRow < rows_.size()) 107 | destinationRow -= 1; 108 | } 109 | if (destinationRow == sourceRow) return false; 110 | 111 | return moveRows(QModelIndex(), sourceRow, 1, QModelIndex(), destinationRow); 112 | } 113 | 114 | Qt::DropActions SegmentView::supportedDropActions() const { 115 | return Qt::MoveAction; 116 | } 117 | 118 | Qt::DropActions SegmentView::supportedDragActions() const { 119 | return Qt::MoveAction; 120 | } 121 | 122 | bool SegmentView::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, 123 | const QModelIndex &destinationParent, int destinationRow) { 124 | if (sourceParent.isValid() || destinationParent.isValid()) return false; 125 | if (count <= 0 || count > 1) return false; 126 | if (sourceRow < 0 || sourceRow + count > rows_.size()) return false; 127 | if (destinationRow < 0 || destinationRow > rows_.size()) return false; 128 | if (destinationRow >= sourceRow && destinationRow <= sourceRow + count) return false; 129 | 130 | if (!beginMoveRows(QModelIndex(), sourceRow, sourceRow + count - 1, 131 | QModelIndex(), destinationRow)) { 132 | return false; 133 | } 134 | 135 | QVector moved; 136 | moved.reserve(count); 137 | for (int i = 0; i < count; ++i) moved.append(rows_.at(sourceRow + i)); 138 | for (int i = 0; i < count; ++i) rows_.removeAt(sourceRow); 139 | 140 | int insertRow = destinationRow; 141 | if (destinationRow > sourceRow) insertRow -= count; 142 | for (int i = 0; i < count; ++i) rows_.insert(insertRow + i, moved.at(i)); 143 | 144 | endMoveRows(); 145 | emit rowReordered(sourceRow, insertRow); 146 | return true; 147 | } 148 | 149 | void SegmentView::setSegments(QVector segments) { 150 | beginResetModel(); 151 | rows_ = std::move(segments); 152 | endResetModel(); 153 | } 154 | 155 | void SegmentView::clear() { 156 | setSegments({}); 157 | } 158 | 159 | QVector SegmentView::segments() const { 160 | return rows_; 161 | } 162 | 163 | QString SegmentView::formatStart(qulonglong value) { 164 | return QStringLiteral("0x%1").arg(QString::number(value, 16).toUpper()); 165 | } 166 | 167 | QString SegmentView::formatEnd(qulonglong start, qulonglong length) { 168 | const qulonglong end = length ? (start + length - 1) : start; 169 | return QStringLiteral("0x%1").arg(QString::number(end, 16).toUpper()); 170 | } 171 | 172 | QString SegmentView::formatSize(qulonglong length) { 173 | return QStringLiteral("%1 (0x%2)") 174 | .arg(QString::number(length), 175 | QString::number(length, 16).toUpper()); 176 | } 177 | 178 | QString SegmentView::formatLabel(const Segment &segment) { 179 | QString label = segment.label; 180 | if (!segment.note.isEmpty()) label += segment.note; 181 | return label; 182 | } 183 | -------------------------------------------------------------------------------- /testdata/file_A_8k.bin: -------------------------------------------------------------------------------- 1 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -------------------------------------------------------------------------------- /testdata/file_B_8k.bin: -------------------------------------------------------------------------------- 1 | BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -------------------------------------------------------------------------------- /testdata/file_C_8k.bin: -------------------------------------------------------------------------------- 1 | CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC -------------------------------------------------------------------------------- /testdata/file_D_8k.bin: -------------------------------------------------------------------------------- 1 | DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD -------------------------------------------------------------------------------- /testdata/file_E_8k.bin: -------------------------------------------------------------------------------- 1 | EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE -------------------------------------------------------------------------------- /testdata/file_F_8k.bin: -------------------------------------------------------------------------------- 1 | FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | if(APPLE) 4 | if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET OR CMAKE_OSX_DEPLOYMENT_TARGET STREQUAL "") 5 | set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum macOS version" FORCE) 6 | endif() 7 | endif() 8 | 9 | # ---- Derive version string ---- 10 | set(_fireminipro_version "0.0.0") 11 | if(EXISTS "${CMAKE_SOURCE_DIR}/.git") 12 | find_package(Git QUIET) 13 | if(GIT_FOUND) 14 | execute_process( 15 | COMMAND "${GIT_EXECUTABLE}" describe --tags --always --match "v[0-9]*" 16 | WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" 17 | OUTPUT_VARIABLE _git_describe 18 | RESULT_VARIABLE _git_result 19 | OUTPUT_STRIP_TRAILING_WHITESPACE 20 | ) 21 | if(_git_result EQUAL 0 AND NOT _git_describe STREQUAL "") 22 | set(_fireminipro_version "${_git_describe}") 23 | endif() 24 | endif() 25 | endif() 26 | 27 | if(_fireminipro_version STREQUAL "0.0.0") 28 | set(_version_file "${CMAKE_SOURCE_DIR}/cmake/version.txt") 29 | if(EXISTS "${_version_file}") 30 | file(READ "${_version_file}" _archived_version) 31 | string(STRIP "${_archived_version}" _archived_version) 32 | if(NOT _archived_version MATCHES "\\$Format") 33 | set(_fireminipro_version "${_archived_version}") 34 | endif() 35 | endif() 36 | endif() 37 | 38 | string(REGEX REPLACE "^v" "" FIREMINIPRO_VERSION_STRING "${_fireminipro_version}") 39 | string(REGEX REPLACE "^v" "" _version_clean "${_fireminipro_version}") 40 | string(REGEX MATCH "^[0-9]+(\\.[0-9]+)*" _numeric_version "${_version_clean}") 41 | if(_numeric_version STREQUAL "") 42 | set(_numeric_version "0.0.0") 43 | endif() 44 | 45 | project(fireminipro VERSION ${_numeric_version} LANGUAGES CXX) 46 | 47 | # --- Qt setup --- 48 | set(CMAKE_CXX_STANDARD 17) 49 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 50 | set(CMAKE_AUTOMOC ON) 51 | set(CMAKE_AUTORCC ON) 52 | 53 | find_package(Qt6 6.2 REQUIRED COMPONENTS Widgets DBus) 54 | 55 | set(SOURCES 56 | src/main.cpp 57 | src/MainWindow.cpp 58 | src/HexView.cpp 59 | src/ProcessHandling.cpp 60 | src/LoadPreviewBar.cpp 61 | src/SegmentView.cpp 62 | src/SegmentTableView.cpp 63 | ) 64 | set(HEADERS 65 | src/MainWindow.h 66 | src/HexView.h 67 | src/ProcessHandling.h 68 | src/LoadPreviewBar.h 69 | src/SegmentView.h 70 | src/SegmentTableView.h 71 | ) 72 | 73 | # Use AUTORCC by listing the qrc directly here. 74 | qt_add_executable(${PROJECT_NAME} MACOSX_BUNDLE 75 | ${SOURCES} ${HEADERS} 76 | resources/icons.qrc 77 | ) 78 | 79 | target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets Qt6::DBus) 80 | # Expose PROJECT_VERSION to C++ as FIREMINIPRO_VERSION 81 | target_compile_definitions(${PROJECT_NAME} 82 | PRIVATE FIREMINIPRO_VERSION="${FIREMINIPRO_VERSION_STRING}") 83 | 84 | 85 | # ------------ macOS app icon (.icns) generation ------------ 86 | # We’ll try to build appicon.icns from resources/appicon.png automatically. 87 | # If the tools aren't found, we fall back to using a prebuilt appicon.icns 88 | # if you place it at resources/appicon.icns. 89 | 90 | if(APPLE) 91 | set(APPICON_PNG "${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.png") 92 | set(APPICON_ICNS "${CMAKE_CURRENT_BINARY_DIR}/appicon.icns") 93 | set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES 94 | "${CMAKE_CURRENT_BINARY_DIR}/appicon.icns" 95 | "${CMAKE_CURRENT_BINARY_DIR}/appicon.iconset" 96 | ) 97 | set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_CLEAN_FILES 98 | "${CMAKE_CURRENT_BINARY_DIR}/appicon.icns" 99 | "${CMAKE_CURRENT_BINARY_DIR}/appicon.iconset" 100 | ) 101 | 102 | # If a prebuilt icns is provided in the source tree, prefer that. 103 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns") 104 | set(APPICON_ICNS "${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns") 105 | else() 106 | # Try to generate .icns from the PNG using sips + iconutil. 107 | find_program(SIPS_EXEC sips) 108 | find_program(ICONUTIL_EXEC iconutil) 109 | 110 | if(SIPS_EXEC AND ICONUTIL_EXEC AND EXISTS "${APPICON_PNG}") 111 | set(ICONSET_DIR "${CMAKE_CURRENT_BINARY_DIR}/appicon.iconset") 112 | 113 | add_custom_command( 114 | OUTPUT "${APPICON_ICNS}" 115 | COMMAND ${CMAKE_COMMAND} -E rm -rf "${ICONSET_DIR}" 116 | COMMAND ${CMAKE_COMMAND} -E make_directory "${ICONSET_DIR}" 117 | 118 | # Create all the standard sizes (1x and 2x) from the base PNG. 119 | # If your base PNG is large (>=1024), quality will be fine. 120 | COMMAND "${SIPS_EXEC}" -z 16 16 "${APPICON_PNG}" --out "${ICONSET_DIR}/icon_16x16.png" 121 | COMMAND "${SIPS_EXEC}" -z 32 32 "${APPICON_PNG}" --out "${ICONSET_DIR}/icon_16x16@2x.png" 122 | COMMAND "${SIPS_EXEC}" -z 32 32 "${APPICON_PNG}" --out "${ICONSET_DIR}/icon_32x32.png" 123 | COMMAND "${SIPS_EXEC}" -z 64 64 "${APPICON_PNG}" --out "${ICONSET_DIR}/icon_32x32@2x.png" 124 | COMMAND "${SIPS_EXEC}" -z 128 128 "${APPICON_PNG}" --out "${ICONSET_DIR}/icon_128x128.png" 125 | COMMAND "${SIPS_EXEC}" -z 256 256 "${APPICON_PNG}" --out "${ICONSET_DIR}/icon_128x128@2x.png" 126 | COMMAND "${SIPS_EXEC}" -z 256 256 "${APPICON_PNG}" --out "${ICONSET_DIR}/icon_256x256.png" 127 | COMMAND "${SIPS_EXEC}" -z 512 512 "${APPICON_PNG}" --out "${ICONSET_DIR}/icon_256x256@2x.png" 128 | COMMAND "${SIPS_EXEC}" -z 512 512 "${APPICON_PNG}" --out "${ICONSET_DIR}/icon_512x512.png" 129 | # 1024x1024 for @2x of 512 130 | COMMAND "${SIPS_EXEC}" -z 1024 1024 "${APPICON_PNG}" --out "${ICONSET_DIR}/icon_512x512@2x.png" 131 | 132 | COMMAND "${ICONUTIL_EXEC}" -c icns "${ICONSET_DIR}" -o "${APPICON_ICNS}" 133 | DEPENDS "${APPICON_PNG}" 134 | VERBATIM 135 | ) 136 | 137 | add_custom_target(AppIcon ALL DEPENDS "${APPICON_ICNS}") 138 | add_dependencies(${PROJECT_NAME} AppIcon) 139 | else() 140 | message(WARNING 141 | "macOS icon generation skipped: 'sips' or 'iconutil' not found, " 142 | "or resources/appicon.png missing. " 143 | "Provide resources/appicon.icns to use a prebuilt icon.") 144 | # If we can't build one, we leave APPICON_ICNS unset unless a prebuilt exists. 145 | unset(APPICON_ICNS) 146 | endif() 147 | endif() 148 | 149 | # If we have an icns (generated or prebuilt), add it to the bundle. 150 | if(APPICON_ICNS) 151 | get_filename_component(_icns_name "${APPICON_ICNS}" NAME) 152 | get_filename_component(_icns_stem "${APPICON_ICNS}" NAME_WE) 153 | # Put the icns into the app bundle's Resources folder 154 | set_source_files_properties("${APPICON_ICNS}" PROPERTIES 155 | MACOSX_PACKAGE_LOCATION "Resources" 156 | ) 157 | target_sources(${PROJECT_NAME} PRIVATE "${APPICON_ICNS}") 158 | 159 | # Tell macOS which icon file to use (filename only) 160 | set_target_properties(${PROJECT_NAME} PROPERTIES 161 | MACOSX_BUNDLE_ICON_FILE "${_icns_stem}" 162 | ) 163 | 164 | endif() 165 | endif() 166 | 167 | # ------------- (optional) nicer bundle metadata ------------- 168 | # set_target_properties(${PROJECT_NAME} PROPERTIES 169 | # MACOSX_BUNDLE_BUNDLE_NAME "fireminipro" 170 | # MACOSX_BUNDLE_GUI_IDENTIFIER "com.example.fireminipro" 171 | # MACOSX_BUNDLE_BUNDLE_VERSION "1.0" 172 | # MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0" 173 | # ) 174 | if(APPLE) 175 | if(NOT DEFINED _icns_name OR _icns_name STREQUAL "") 176 | set(_icns_name "appicon.icns") 177 | endif() 178 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 179 | COMMAND ${CMAKE_COMMAND} -E copy_if_different "${APPICON_ICNS}" "$/Contents/Resources/${_icns_name}" 180 | COMMAND ${CMAKE_COMMAND} -E touch "$/Contents/Resources/${_icns_name}" 181 | COMMAND ${CMAKE_COMMAND} -E touch "$/Contents/Info.plist" 182 | COMMAND ${CMAKE_COMMAND} -E touch $ 183 | COMMAND /bin/sh -c "/usr/bin/osascript -e 'tell application \\\"Finder\\\" to update POSIX file \\\"$\\\"' || true" 184 | COMMENT "Touching bundle assets to refresh Finder icon" 185 | ) 186 | endif() 187 | -------------------------------------------------------------------------------- /.github/workflows/appimage.yml: -------------------------------------------------------------------------------- 1 | name: Linux AppImage 2 | on: 3 | push: 4 | tags: ["v*"] 5 | workflow_dispatch: 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-22.04 10 | steps: 11 | - uses: actions/checkout@v4 12 | # Ensures full git history for git describe 13 | with: 14 | fetch-depth: 0 15 | fetch-tags: true 16 | 17 | - name: Install deps 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y build-essential ninja-build cmake libgl1-mesa-dev patchelf \ 21 | pkg-config libusb-1.0-0-dev zlib1g-dev \ 22 | qt6-base-dev qt6-base-dev-tools qt6-tools-dev qt6-tools-dev-tools 23 | 24 | - name: Make Qt6 qmake discoverable 25 | run: | 26 | if [ -x /usr/lib/qt6/bin/qmake6 ]; then 27 | sudo ln -sf /usr/lib/qt6/bin/qmake6 /usr/local/bin/qmake 28 | sudo ln -sf /usr/lib/qt6/bin/qmake6 /usr/lib/qt6/bin/qmake 29 | elif [ -x /usr/lib/qt6/bin/qmake ]; then 30 | sudo ln -sf /usr/lib/qt6/bin/qmake /usr/local/bin/qmake 31 | fi 32 | qmake -v || true 33 | 34 | - name: Determine version 35 | id: version 36 | run: | 37 | # Use git describe to get the version 38 | ver=$(git describe --tags --always --match "v[0-9]*" 2>/dev/null || echo "${GITHUB_REF_NAME}") 39 | # Remove leading v if present 40 | ver="${ver#v}" 41 | # Remove hash at end (for tagged builds) 42 | ver="${ver%%-g*}" 43 | echo "VERSION=$ver" >> $GITHUB_ENV 44 | echo "Determined version: $ver" 45 | 46 | - name: Build 47 | run: | 48 | cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/lib/qt6 49 | ninja -C build 50 | 51 | - name: Build minipro CLI 52 | env: 53 | MINIPRO_VERSION: 0.7.4 54 | MINIPRO_STAGING: ${{ github.workspace }}/minipro-staging 55 | run: | 56 | curl -L "https://gitlab.com/DavidGriffith/minipro/-/archive/${MINIPRO_VERSION}/minipro-${MINIPRO_VERSION}.tar.bz2" -o minipro.tar.bz2 57 | tar xf minipro.tar.bz2 58 | cd "minipro-${MINIPRO_VERSION}" 59 | sed -i 's#^UDEV_DIR=#UDEV_DIR?=#' Makefile 60 | sed -i 's#^COMPLETIONS_DIR=#COMPLETIONS_DIR?=#' Makefile 61 | sed -i 's#^SHARE_INSTDIR=$(DESTDIR)$(PREFIX)/share/minipro#SHARE_INSTDIR?=$(PREFIX)/share/minipro#' Makefile 62 | sed -i '/^SHARE_INSTDIR?=$(PREFIX)\/share\/minipro/a INSTALL_SHARE_INSTDIR=$(DESTDIR)$(SHARE_INSTDIR)' Makefile 63 | sed -i 's#mkdir -p $(SHARE_INSTDIR)#mkdir -p $(INSTALL_SHARE_INSTDIR)#' Makefile 64 | sed -i 's#cp $(ALGORITHM) $(SHARE_INSTDIR)#cp $(ALGORITHM) $(INSTALL_SHARE_INSTDIR)#' Makefile 65 | sed -i 's#cp $(INFOIC) $(SHARE_INSTDIR)#cp $(INFOIC) $(INSTALL_SHARE_INSTDIR)#' Makefile 66 | sed -i 's#cp $(LOGICIC) $(SHARE_INSTDIR)#cp $(LOGICIC) $(INSTALL_SHARE_INSTDIR)#' Makefile 67 | sed -i 's#rm -f $(SHARE_INSTDIR)#rm -f $(INSTALL_SHARE_INSTDIR)#' Makefile 68 | make PREFIX=/usr SHARE_INSTDIR=/usr/share/minipro UDEV_DIR= COMPLETIONS_DIR= 69 | make PREFIX=/usr DESTDIR="$MINIPRO_STAGING" SHARE_INSTDIR=/usr/share/minipro UDEV_DIR= COMPLETIONS_DIR= install 70 | 71 | - name: Fetch linuxdeploy tooling 72 | run: | 73 | wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage 74 | wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage 75 | chmod +x linuxdeploy-x86_64.AppImage linuxdeploy-plugin-qt-x86_64.AppImage 76 | 77 | - name: Bundle AppImage 78 | env: 79 | QMAKE: /usr/lib/qt6/bin/qmake6 80 | LINUXDEPLOY_QT_QMAKE: ${{ github.workspace }}/ci-tools/qmake 81 | LINUXDEPLOY_PLUGIN_QT_QMAKE: ${{ github.workspace }}/ci-tools/qmake 82 | LINUXDEPLOY_QT_AUTO_INCLUDE_MODULES: 1 83 | QT_SELECT: qt6 84 | APPIMAGE_EXTRACT_AND_RUN: 1 85 | VERSION: ${{ env.VERSION }} 86 | run: | 87 | export PATH=/usr/lib/qt6/bin:$PATH 88 | export QMAKE=/usr/lib/qt6/bin/qmake6 89 | mkdir -p ci-tools 90 | cat <<'EOF' > ci-tools/qmake 91 | #!/usr/bin/env bash 92 | exec /usr/lib/qt6/bin/qmake6 "$@" 93 | EOF 94 | chmod +x ci-tools/qmake 95 | export PATH=$PWD/ci-tools:$PATH 96 | export LINUXDEPLOY_QT_QMAKE=$PWD/ci-tools/qmake 97 | export LINUXDEPLOY_PLUGIN_QT_QMAKE=$PWD/ci-tools/qmake 98 | mkdir -p AppDir/usr/bin AppDir/usr/share/applications AppDir/usr/share/icons/hicolor/256x256/apps 99 | cp build/fireminipro AppDir/usr/bin/ 100 | MINIPRO_STAGING="$GITHUB_WORKSPACE/minipro-staging/usr" 101 | if [ -f "$MINIPRO_STAGING/bin/minipro" ]; then 102 | cp "$MINIPRO_STAGING/bin/minipro" AppDir/usr/bin/ 103 | chmod +x AppDir/usr/bin/minipro 104 | if [ -d "$MINIPRO_STAGING/share/minipro" ]; then 105 | mkdir -p AppDir/usr/share 106 | cp -R "$MINIPRO_STAGING/share/minipro" AppDir/usr/share/ 107 | fi 108 | if [ -d "$MINIPRO_STAGING/share/doc" ]; then 109 | mkdir -p AppDir/usr/share/doc 110 | cp -R "$MINIPRO_STAGING/share/doc" AppDir/usr/share/doc/ 111 | fi 112 | if [ -d "$MINIPRO_STAGING/share/licenses" ]; then 113 | mkdir -p AppDir/usr/share/licenses 114 | cp -R "$MINIPRO_STAGING/share/licenses" AppDir/usr/share/licenses/ 115 | fi 116 | fi 117 | mkdir -p AppDir/usr/share/doc/fireminipro/thirdparty 118 | cp thirdparty/LICENSE_* AppDir/usr/share/doc/fireminipro/thirdparty/ 119 | cp thirdparty/README_*.md AppDir/usr/share/doc/fireminipro/thirdparty/ 120 | cp thirdparty/*.tar.bz2 AppDir/usr/share/doc/fireminipro/thirdparty/ 121 | printf '%s\n' \ 122 | '[Desktop Entry]' \ 123 | 'Type=Application' \ 124 | 'Name=FireMinipro' \ 125 | 'Exec=fireminipro' \ 126 | 'Icon=fireminipro' \ 127 | 'Categories=Development;Utility;' > AppDir/usr/share/applications/fireminipro.desktop 128 | cp resources/appicon.png AppDir/usr/share/icons/hicolor/256x256/apps/fireminipro.png 129 | extra_flags=() 130 | if [ -f AppDir/usr/bin/minipro ]; then 131 | extra_flags+=(--executable AppDir/usr/bin/minipro) 132 | fi 133 | # Build AppImage with correct versioned filename 134 | ./linuxdeploy-x86_64.AppImage --appdir AppDir \ 135 | --executable AppDir/usr/bin/fireminipro \ 136 | -d AppDir/usr/share/applications/fireminipro.desktop \ 137 | -i AppDir/usr/share/icons/hicolor/256x256/apps/fireminipro.png \ 138 | "${extra_flags[@]}" \ 139 | --plugin qt --output appimage 140 | # Rename the output file 141 | src=$(ls FireMinipro*.AppImage | head -n 1) 142 | dst="FireMinipro-${VERSION}-x86_64.AppImage" 143 | if [ "$src" != "$dst" ]; then 144 | mv "$src" "$dst" 145 | fi 146 | # Clean up 147 | rm -f linuxdeploy-x86_64.AppImage linuxdeploy-plugin-qt-x86_64.AppImage 148 | 149 | - name: Determine release tag 150 | id: release 151 | run: | 152 | if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then 153 | TAG_NAME="${GITHUB_REF_NAME}" 154 | if ! git rev-parse "$TAG_NAME" >/dev/null 2>&1; then 155 | git tag "$TAG_NAME" 156 | git push origin "$TAG_NAME" 157 | fi 158 | else 159 | TAG_NAME="${GITHUB_REF_NAME}" 160 | fi 161 | echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT 162 | echo "Release tag: $TAG_NAME" 163 | 164 | - name: Upload release asset 165 | uses: softprops/action-gh-release@v2 166 | with: 167 | files: FireMinipro-${{ env.VERSION }}-x86_64.AppImage 168 | tag_name: ${{ steps.release.outputs.tag_name }} 169 | env: 170 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 171 | -------------------------------------------------------------------------------- /thirdparty/LICENSE_qt6: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /src/LoadPreviewBar.cpp: -------------------------------------------------------------------------------- 1 | #include "LoadPreviewBar.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | LoadPreviewBar::LoadPreviewBar(QWidget *parent) : QWidget(parent) { 11 | setMinimumHeight(120); 12 | } 13 | 14 | void LoadPreviewBar::setParams(qulonglong bufSize, qulonglong off, qulonglong dataLen, qulonglong padLen) { 15 | bufSize_ = bufSize; 16 | off_ = off; 17 | dataLen_ = dataLen; 18 | padLen_ = padLen; 19 | update(); 20 | } 21 | 22 | void LoadPreviewBar::setBufferSegments(QVector> segments) { 23 | bufferSegments_ = std::move(segments); 24 | update(); 25 | } 26 | 27 | QSize LoadPreviewBar::sizeHint() const { 28 | return QSize(420, 120); 29 | } 30 | 31 | void LoadPreviewBar::paintEvent(QPaintEvent *event) { 32 | QWidget::paintEvent(event); 33 | 34 | QPainter p(this); 35 | p.setRenderHint(QPainter::Antialiasing, false); 36 | 37 | const QPalette pal = palette(); 38 | const QColor windowColor = pal.color(QPalette::Window); 39 | const QColor baseColor = pal.color(QPalette::Base); 40 | const QColor alternateColor = pal.color(QPalette::AlternateBase); 41 | QColor frameColor = pal.color(QPalette::Mid); 42 | QColor textColor = pal.color(QPalette::WindowText); 43 | if (!frameColor.isValid()) frameColor = pal.color(QPalette::Dark); 44 | if (!frameColor.isValid()) frameColor = textColor; 45 | if (!textColor.isValid()) textColor = Qt::black; 46 | 47 | const bool darkTheme = windowColor.lightness() < textColor.lightness(); 48 | 49 | auto adjustTone = [&](QColor color, int lighterFactor, int darkerFactor) { 50 | if (!color.isValid()) color = baseColor.isValid() ? baseColor : windowColor; 51 | color = darkTheme ? color.lighter(lighterFactor) : color.darker(darkerFactor); 52 | color.setAlpha(255); 53 | return color; 54 | }; 55 | auto ensureContrast = [&](QColor color, const QColor &reference, int lighterFactor, int darkerFactor) { 56 | if (!color.isValid() || color == reference) { 57 | return adjustTone(reference, lighterFactor, darkerFactor); 58 | } 59 | return color; 60 | }; 61 | 62 | const QColor emptyColor = adjustTone(baseColor.isValid() ? baseColor : windowColor, 108, 103); 63 | const QColor bufferColor = ensureContrast(alternateColor, emptyColor, 125, 115); 64 | const QColor dataColor = darkTheme ? QColor(90, 180, 130) : QColor(120, 200, 120); 65 | const QColor paddingColor = darkTheme ? QColor(190, 160, 80) : QColor(250, 220, 120); 66 | QColor overlapColor = darkTheme ? QColor(220, 110, 110) : QColor(220, 80, 80); 67 | overlapColor.setAlpha(255); 68 | frameColor = ensureContrast(frameColor, emptyColor, 140, 120); 69 | QColor tickColor = frameColor; 70 | 71 | const int W = width(); 72 | const int barH = 16; 73 | const int topMargin = 28; 74 | const int y = topMargin; // bar top 75 | 76 | // Determine total span to visualize 77 | qulonglong total = bufSize_; 78 | qulonglong prePadLen = 0; 79 | if (off_ > bufSize_) prePadLen = off_ - bufSize_; 80 | const qulonglong newEnd = (off_ + dataLen_ + padLen_); 81 | if (newEnd > total) total = newEnd; 82 | if (total == 0) { 83 | p.fillRect(0, y, W, barH, emptyColor); 84 | p.setPen(frameColor); 85 | p.drawRect(0, y, W-1, barH); 86 | p.setPen(textColor); 87 | p.drawText(6, y+barH+16, tr("(empty)")); 88 | return; 89 | } 90 | 91 | auto xFor = [&](qulonglong v){ return int((double(v) / double(total)) * (W-2)) + 1; }; 92 | 93 | // Background (gap/empty) light gray 94 | p.fillRect(0, y, W, barH, emptyColor); 95 | p.setPen(frameColor); 96 | p.drawRect(0, y, W-1, barH); 97 | 98 | // Existing buffer region [0, bufSize_) 99 | if (bufSize_ > 0) { 100 | int x0 = xFor(0), x1 = xFor(bufSize_); 101 | p.fillRect(x0, y, qMax(1, x1-x0), barH, bufferColor); 102 | } 103 | 104 | // Pre-padding from buffer end to offset (if any) 105 | if (prePadLen > 0) { 106 | int x0 = xFor(bufSize_); 107 | int x1 = xFor(off_); 108 | p.fillRect(x0, y, qMax(1, x1 - x0), barH, paddingColor); 109 | } 110 | 111 | // New data region [off_, off_+dataLen_) 112 | if (dataLen_ > 0) { 113 | int x0 = xFor(qMin(off_, total)); 114 | int x1 = xFor(qMin(off_ + dataLen_, total)); 115 | p.fillRect(x0, y, qMax(1, x1-x0), barH, dataColor); 116 | } 117 | 118 | bool hasOverlap = false; 119 | qulonglong ovStart = 0; 120 | qulonglong ovEnd = 0; 121 | 122 | // Overlap: portion of new data that overwrites existing buffer [0, bufSize_) 123 | if (dataLen_ > 0 && bufSize_ > 0) { 124 | const qulonglong dataStart = off_; 125 | const qulonglong dataEnd = off_ + dataLen_; 126 | // True intersection of [dataStart, dataEnd) with [0, bufSize_) 127 | ovStart = std::max(dataStart, 0); 128 | ovEnd = std::min(dataEnd, bufSize_); 129 | if (ovEnd > ovStart) { 130 | int xr0 = xFor(ovStart); 131 | int xr1 = xFor(ovEnd); 132 | QColor red = overlapColor; 133 | red.setAlpha(180); 134 | p.fillRect(xr0, y, qMax(1, xr1 - xr0), barH, red); 135 | hasOverlap = true; 136 | } 137 | } 138 | 139 | // Padding region [off_+dataLen_, off_+dataLen_+padLen_) 140 | if (padLen_ > 0) { 141 | int x0 = xFor(qMin(off_ + dataLen_, total)); 142 | int x1 = xFor(qMin(off_ + dataLen_ + padLen_, total)); 143 | p.fillRect(x0, y, qMax(1, x1-x0), barH, paddingColor); 144 | } 145 | 146 | // Existing buffer segments markers (thin vertical lines at each start, except first) 147 | if (bufferSegments_.size() > 1) { 148 | const int lineTop = y + 1; 149 | const int lineBottom = y + barH - 2; 150 | QColor markerColor = frameColor; 151 | markerColor.setAlpha(180); 152 | QPen segmentPen(markerColor, 1); 153 | p.setPen(segmentPen); 154 | for (int i = 1; i < bufferSegments_.size(); ++i) { 155 | const auto &segment = bufferSegments_.at(i); 156 | qulonglong segStart = segment.first; 157 | if (segStart >= total) continue; 158 | int x = xFor(segStart); 159 | p.drawLine(x, lineTop, x, lineBottom); 160 | } 161 | p.setPen(frameColor); 162 | } 163 | 164 | // Address markers: numbers above the bar + legend below 165 | struct AddressMarker { 166 | qulonglong value = 0; 167 | int x = 0; 168 | QString text; 169 | bool top = true; 170 | int textX = 0; 171 | }; 172 | const int edgeMargin = 2; 173 | const int tickLength = 7; 174 | QVector markers; 175 | const int leftEdge = 0; 176 | const int rightEdge = W - 1; 177 | 178 | auto addMarker = [&](qulonglong value) { 179 | if (total == 0) return; 180 | if (value > total) value = total; 181 | if (value > 0 && value == total) value = total - 1; 182 | if (qint64(value) < 0) value = 0; 183 | AddressMarker marker; 184 | marker.value = value; 185 | if (value == 0) { 186 | marker.x = leftEdge; 187 | } else if (value == total - 1) { 188 | marker.x = rightEdge; 189 | } else { 190 | marker.x = std::clamp(xFor(value), leftEdge, rightEdge); 191 | } 192 | marker.text = QStringLiteral("0x") + QString::number(value, 16).toUpper(); 193 | markers.append(std::move(marker)); 194 | }; 195 | 196 | addMarker(0); 197 | if (bufSize_ > 0) addMarker(bufSize_ - 1); 198 | if (prePadLen > 0 && off_ > 0) addMarker(off_ - 1); 199 | if (dataLen_ > 0) { 200 | addMarker(off_); 201 | addMarker(off_ + dataLen_ - 1); 202 | } 203 | if (ovEnd > ovStart) { 204 | addMarker(ovStart); 205 | addMarker(ovEnd - 1); 206 | } 207 | if (padLen_ > 0) { 208 | addMarker(off_ + dataLen_); 209 | addMarker(off_ + dataLen_ + padLen_ - 1); 210 | } 211 | if (total > 0) addMarker(total - 1); 212 | 213 | std::sort(markers.begin(), markers.end(), [](const AddressMarker &a, const AddressMarker &b) { 214 | if (a.value == b.value) return a.x < b.x; 215 | return a.value < b.value; 216 | }); 217 | QVector deduped; 218 | deduped.reserve(markers.size()); 219 | int lastX = -1; 220 | qulonglong lastValue = std::numeric_limits::max(); 221 | for (const auto &marker : markers) { 222 | if (!deduped.isEmpty() && marker.value == lastValue) continue; 223 | if (!deduped.isEmpty() && std::abs(marker.x - lastX) <= 1) continue; 224 | deduped.append(marker); 225 | lastValue = marker.value; 226 | lastX = marker.x; 227 | } 228 | markers = deduped; 229 | 230 | QFont markerFont = p.font(); 231 | markerFont.setBold(false); 232 | markerFont.setPointSizeF(markerFont.pointSizeF() - 1.5); 233 | p.setFont(markerFont); 234 | const QFontMetrics markerMetrics(markerFont); 235 | const int topBaseline = y - tickLength - 2; 236 | const int bottomTextTop = y + barH + tickLength + 2; 237 | const int bottomBaseline = bottomTextTop + markerMetrics.ascent(); 238 | 239 | const int overlapGap = 2; 240 | int lastTopRight = edgeMargin - overlapGap - 3; 241 | int lastBottomRight = edgeMargin - overlapGap - 3; 242 | for (int i = 0; i < markers.size(); ++i) { 243 | const QString number = QString::number(i + 1); 244 | const int textWidth = markerMetrics.horizontalAdvance(number); 245 | const int anchor = markers[i].x; 246 | 247 | int topTextX = anchor - textWidth / 2; 248 | if (i == 0) { 249 | topTextX = std::max(edgeMargin, anchor - textWidth + 1); 250 | } else { 251 | if (topTextX < edgeMargin) topTextX = edgeMargin; 252 | } 253 | if (topTextX + textWidth > W - edgeMargin) topTextX = W - edgeMargin - textWidth; 254 | bool topOverlap = (topTextX <= lastTopRight + overlapGap); 255 | 256 | int bottomTextX = anchor - textWidth / 2; 257 | if (bottomTextX + textWidth > W - edgeMargin) bottomTextX = W - edgeMargin - textWidth; 258 | if (i == markers.size() - 1) { 259 | bottomTextX = std::min(W - edgeMargin - textWidth, anchor - 1); 260 | } 261 | if (bottomTextX < edgeMargin) bottomTextX = edgeMargin; 262 | bool bottomOverlap = (bottomTextX <= lastBottomRight + overlapGap); 263 | 264 | bool placeTop = true; 265 | if (topOverlap && !bottomOverlap) placeTop = false; 266 | else if (!topOverlap && bottomOverlap) placeTop = true; 267 | else if (topOverlap && bottomOverlap) { 268 | int spaceTop = topTextX - edgeMargin; 269 | int spaceBottom = (W - edgeMargin) - (bottomTextX + textWidth); 270 | placeTop = spaceTop >= spaceBottom; 271 | } 272 | 273 | markers[i].top = placeTop; 274 | if (placeTop) { 275 | if (topTextX <= lastTopRight + overlapGap) { 276 | topTextX = lastTopRight + overlapGap + 1; 277 | if (topTextX + textWidth > W - edgeMargin) { 278 | topTextX = W - edgeMargin - textWidth; 279 | } 280 | } 281 | markers[i].textX = topTextX; 282 | lastTopRight = topTextX + textWidth; 283 | } else { 284 | if (bottomTextX <= lastBottomRight + overlapGap) { 285 | bottomTextX = lastBottomRight + overlapGap + 1; 286 | if (bottomTextX + textWidth > W - edgeMargin) { 287 | bottomTextX = W - edgeMargin - textWidth; 288 | } 289 | if (bottomTextX < edgeMargin) bottomTextX = edgeMargin; 290 | } 291 | markers[i].textX = bottomTextX; 292 | lastBottomRight = bottomTextX + textWidth; 293 | } 294 | } 295 | 296 | p.setPen(tickColor); 297 | for (int i = 0; i < markers.size(); ++i) { 298 | const QString number = QString::number(i + 1); 299 | const int anchor = markers[i].x; 300 | if (markers[i].top) { 301 | p.drawLine(anchor, y, anchor, y - tickLength); 302 | p.setPen(textColor); 303 | p.drawText(markers[i].textX, topBaseline, number); 304 | p.setPen(tickColor); 305 | } else { 306 | p.drawLine(anchor, y + barH, anchor, y + barH + tickLength); 307 | p.setPen(textColor); 308 | p.drawText(markers[i].textX, bottomBaseline, number); 309 | p.setPen(tickColor); 310 | } 311 | } 312 | 313 | p.setFont(markerFont); 314 | const int markerBlockHeight = markerMetrics.height() + tickLength + 4; 315 | const int legendTop = y + barH + markerBlockHeight + 4; 316 | 317 | p.setBrush(Qt::NoBrush); 318 | QFont legendFont = p.font(); 319 | legendFont.setBold(false); 320 | legendFont.setPointSizeF(legendFont.pointSizeF() - 1); 321 | p.setFont(legendFont); 322 | const QFontMetrics legendMetrics(legendFont); 323 | int addrLy = legendTop + legendMetrics.ascent(); 324 | int addrLx = 4; 325 | const QString bufferLabel = tr("buffer"); 326 | const QString dataLabel = tr("data"); 327 | const QString paddingLabel = tr("padding"); 328 | const QString overlapLabel = tr("overlap"); 329 | const bool hasBufferSegment = bufSize_ > 0; 330 | bool hasDataSegment = false; 331 | if (dataLen_ > 0) { 332 | const qulonglong dataStart = off_; 333 | const qulonglong dataEnd = off_ + dataLen_; 334 | qulonglong overlapLen = 0; 335 | if (ovEnd > ovStart) { 336 | const qulonglong overlapStart = std::max(ovStart, dataStart); 337 | const qulonglong overlapEnd = std::min(ovEnd, dataEnd); 338 | if (overlapEnd > overlapStart) overlapLen = overlapEnd - overlapStart; 339 | } 340 | hasDataSegment = dataLen_ > overlapLen; 341 | } 342 | const bool hasPaddingSegment = (prePadLen > 0) || (padLen_ > 0); 343 | QStringList activeFields; 344 | if (hasBufferSegment) activeFields << bufferLabel; 345 | if (hasDataSegment) activeFields << dataLabel; 346 | if (hasPaddingSegment) activeFields << paddingLabel; 347 | if (hasOverlap) activeFields << overlapLabel; 348 | 349 | QFont legendBold = legendFont; 350 | legendBold.setBold(true); 351 | const QFontMetrics numberMetrics(legendBold); 352 | 353 | for (int i = 0; i < markers.size(); ++i) { 354 | const QString numberLabel = QString::number(i + 1); 355 | const QString addressText = markers[i].text; 356 | const QString suffix = QStringLiteral(": %1").arg(addressText); 357 | const int numberWidth = numberMetrics.horizontalAdvance(numberLabel); 358 | const int suffixWidth = legendMetrics.horizontalAdvance(suffix); 359 | 360 | p.setPen(textColor); 361 | p.setFont(legendBold); 362 | p.drawText(addrLx, addrLy, numberLabel); 363 | p.setFont(legendFont); 364 | p.drawText(addrLx + numberWidth, addrLy, suffix); 365 | 366 | addrLx += numberWidth + suffixWidth + 16; 367 | } 368 | 369 | int ly = addrLy + legendMetrics.height() + 8; 370 | auto legend = [&](QColor c, const QString &t, int &lx){ 371 | p.fillRect(lx, ly-10, 10, 10, c); 372 | p.setPen(frameColor); 373 | p.drawRect(lx, ly-10, 10, 10); 374 | p.setPen(textColor); 375 | p.drawText(lx+14, ly, t); 376 | lx += 14 + p.fontMetrics().horizontalAdvance(t) + 12; 377 | }; 378 | int lx = 4; 379 | for (const QString &label : activeFields) { 380 | if (label == bufferLabel) legend(bufferColor, label, lx); 381 | else if (label == dataLabel) legend(dataColor, label, lx); 382 | else if (label == paddingLabel) legend(paddingColor, label, lx); 383 | else if (label == overlapLabel) legend(overlapColor, label, lx); 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /src/ProcessHandling.cpp: -------------------------------------------------------------------------------- 1 | // src/ProcessHandling.cpp 2 | #include "ProcessHandling.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // Constructor 13 | ProcessHandling::ProcessHandling(QObject *parent) 14 | : QObject(parent) 15 | { 16 | connect(&process_, &QProcess::readyReadStandardOutput, 17 | this, &ProcessHandling::handleStdout); 18 | connect(&process_, QOverload::of(&QProcess::finished), 19 | this, &ProcessHandling::handleFinished); 20 | connect(&process_, &QProcess::errorOccurred, this, [this](QProcess::ProcessError e){ 21 | // if process was killed, do not log an error 22 | if (e == QProcess::ProcessError::Crashed) return; 23 | emit errorLine(QString("[QProcess error] %1").arg(static_cast(e))); 24 | emit finished(-1, QProcess::CrashExit); 25 | }); 26 | 27 | mode_ = Mode::Idle; 28 | stdoutBuffer_.clear(); 29 | stdoutFragment_.clear(); 30 | } 31 | 32 | QString ProcessHandling::resolveMiniproPath() { 33 | QString bin = QStandardPaths::findExecutable(QStringLiteral("minipro")); 34 | QStringList candidates; 35 | 36 | const QString appDir = qEnvironmentVariable("APPDIR"); 37 | if (!appDir.isEmpty()) { 38 | candidates << (appDir + "/usr/bin/minipro"); 39 | } 40 | 41 | const QString execDir = QCoreApplication::applicationDirPath(); 42 | QString bundledShareDir; 43 | if (!execDir.isEmpty()) { 44 | candidates << (execDir + "/minipro"); 45 | #ifdef Q_OS_MAC 46 | candidates << (execDir + "/../Resources/minipro"); 47 | QDir resDir(execDir); 48 | if (resDir.cd("../Resources/minipro")) { 49 | bundledShareDir = resDir.canonicalPath(); 50 | } 51 | #endif 52 | if (bundledShareDir.isEmpty()) { 53 | QDir shareDir(execDir); 54 | if (shareDir.cd("../share/minipro")) { 55 | bundledShareDir = shareDir.canonicalPath(); 56 | } 57 | } 58 | } 59 | 60 | if (bundledShareDir.isEmpty() && !appDir.isEmpty()) { 61 | const QString shareCandidate = appDir + "/usr/share/minipro"; 62 | if (QFileInfo::exists(shareCandidate)) bundledShareDir = shareCandidate; 63 | } 64 | 65 | if (!bundledShareDir.isEmpty()) { 66 | qputenv("MINIPRO_HOME", bundledShareDir.toUtf8()); 67 | } 68 | 69 | candidates << QStringLiteral("/opt/homebrew/bin/minipro") 70 | << QStringLiteral("/usr/local/bin/minipro") 71 | << QStringLiteral("/usr/bin/minipro"); 72 | 73 | for (const QString &candidate : candidates) { 74 | QFileInfo info(candidate); 75 | if (info.exists() && info.isExecutable()) { 76 | bin = info.absoluteFilePath(); 77 | break; 78 | } 79 | } 80 | 81 | if (bin.isEmpty()) bin = "minipro"; 82 | return bin; 83 | } 84 | 85 | // Parse programmer list output from minipro -k 86 | QStringList ProcessHandling::parseProgrammerList(const QString &text) const { 87 | QStringList out; 88 | 89 | // New-format: "Programmer N: TL866A; ..." 90 | QRegularExpression reNew(R"(^\s*Programmer\s+\d+\s*:\s*([^;]+);)", QRegularExpression::MultilineOption); 91 | auto it = reNew.globalMatch(text); 92 | while (it.hasNext()) { 93 | auto m = it.next(); 94 | const QString name = m.captured(1).trimmed(); 95 | if (!name.isEmpty()) out << name; 96 | } 97 | 98 | // Old-format: "t48: T48" or "tl866a: TL866A" 99 | QRegularExpression reOld(R"(^\s*([^\s:]+)\s*:\s*[^\n;]+)", QRegularExpression::MultilineOption); 100 | auto it2 = reOld.globalMatch(text); 101 | while (it2.hasNext()) { 102 | auto m = it2.next(); 103 | QString name = m.captured(1).trimmed(); 104 | name = name.toUpper(); 105 | if (!name.isEmpty() && !out.contains(name)) out << name; 106 | } 107 | 108 | // Filter obvious non-device lines 109 | out.erase(std::remove_if(out.begin(), out.end(), [](const QString &s){ 110 | const QString t = s.toLower(); 111 | return t.contains("no programmer found") || t.startsWith("share dir") || t.startsWith("supported"); 112 | }), out.end()); 113 | 114 | out.removeDuplicates(); 115 | return out; 116 | } 117 | 118 | // Parse chip info output from minipro -q -d "" 119 | ProcessHandling::ChipInfo ProcessHandling::parseChipInfo(const QString &text) const 120 | { 121 | ChipInfo ci; 122 | ci.raw = text; 123 | 124 | auto rxLine = [](const QString &label){ 125 | return QRegularExpression("^\\s*" + QRegularExpression::escape(label) + "\\s*:\\s*(.+)\\s*$", 126 | QRegularExpression::MultilineOption); 127 | }; 128 | auto cap1 = [&](const QRegularExpression &rx) -> QString { 129 | auto m = rx.match(text); 130 | return m.hasMatch() ? m.captured(1).trimmed() : QString{}; 131 | }; 132 | 133 | // Name: "AM2764A@DIP28" or just "AM2764A" 134 | { 135 | const QString nameLine = cap1(rxLine("Name")); 136 | if (!nameLine.isEmpty()) { 137 | const int at = nameLine.indexOf('@'); 138 | if (at >= 0) { 139 | ci.baseName = nameLine.left(at).trimmed(); 140 | ci.package = nameLine.mid(at + 1).trimmed(); 141 | } else { 142 | ci.baseName = nameLine.trimmed(); 143 | } 144 | } 145 | } 146 | 147 | // Memory: "8192 Bytes" or "262144 Words" 148 | { 149 | QRegularExpression rxMem(R"(^\s*Memory\s*:\s*([0-9]+)\s*(Bytes?|Words?)\s*$)", 150 | QRegularExpression::MultilineOption | QRegularExpression::CaseInsensitiveOption); 151 | auto m = rxMem.match(text); 152 | if (m.hasMatch()) { 153 | const qulonglong val = m.captured(1).toULongLong(); 154 | const QString unit = m.captured(2).toLower(); 155 | if (unit.startsWith("word")) { 156 | ci.bytes = val * 2; // words -> bytes 157 | ci.wordBits = 16; 158 | } else { 159 | ci.bytes = val; 160 | ci.wordBits = 8; 161 | } 162 | } 163 | } 164 | 165 | // Logic: "Vector count: 10" (logic ICs won’t have a Memory line) 166 | { 167 | static QRegularExpression reVec(R"(^\s*Vector count:\s*([0-9]+))", 168 | QRegularExpression::MultilineOption); 169 | auto m = reVec.match(text); 170 | if (m.hasMatch()) { 171 | ci.isLogic = true; 172 | bool ok=false; 173 | ci.vectorCount = m.captured(1).toInt(&ok); 174 | if (!ok) ci.vectorCount = 0; 175 | } 176 | } 177 | 178 | // Protocol: "0x07" 179 | { 180 | const QString proto = cap1(rxLine("Protocol")); 181 | if (!proto.isEmpty()) ci.protocol = proto; 182 | } 183 | 184 | // Read/Write buffer sizes: "Read buffer size: 1024 Bytes", "Write buffer size: 128 Bytes" 185 | auto parseSize = [&](const QString &label) -> qulonglong { 186 | QRegularExpression rx(QString(R"(^\s*)") + QRegularExpression::escape(label) + 187 | R"(\s*:\s*([0-9]+))", 188 | QRegularExpression::MultilineOption | QRegularExpression::CaseInsensitiveOption); 189 | auto m = rx.match(text); 190 | return m.hasMatch() ? m.captured(1).toULongLong() : 0ull; 191 | }; 192 | ci.readBuf = parseSize("Read buffer size"); 193 | ci.writeBuf = parseSize("Write buffer size"); 194 | 195 | return ci; 196 | } 197 | 198 | // Start the minipro process and make sure only one instance is running 199 | void ProcessHandling::startMinipro(Mode mode, const QStringList& args) 200 | { 201 | // If something is still running, stop it (keeps current behavior) 202 | if (mode_ != Mode::Idle || process_.state() == QProcess::Running) { 203 | process_.kill(); 204 | process_.waitForFinished(3000); 205 | } 206 | 207 | const QString bin = resolveMiniproPath(); 208 | 209 | // Log the exact command line we’re about to run 210 | emit logLine(QString("[Run] %1 %2").arg(bin, args.join(' '))); 211 | 212 | // Set mode first, then clear any previous buffered output 213 | mode_ = mode; 214 | stdoutBuffer_.clear(); 215 | stdoutFragment_.clear(); 216 | 217 | // Unified QProcess setup 218 | process_.setProgram(bin); 219 | process_.setArguments(args); 220 | process_.setProcessChannelMode(QProcess::MergedChannels); 221 | process_.start(); 222 | emit started(); 223 | } 224 | 225 | // Helper to create a unique temp path for reading 226 | static QString uniqueTempPath(const QString& base = "fireminipro-read") 227 | { 228 | const QString tmpRoot = 229 | QStandardPaths::writableLocation(QStandardPaths::TempLocation); 230 | const QString ts = QDateTime::currentDateTime().toString("yyMMdd-hhmmss"); 231 | return QDir(tmpRoot).filePath(base + "-" + ts + ".bin"); 232 | } 233 | 234 | // Read from chip to a unique temp file, emit readReady() with path when done 235 | void ProcessHandling::readChipImage(const QString& programmer, 236 | const QString& device, 237 | const QStringList& extraFlags) 238 | { 239 | // Parse device name without @ending, if one exists 240 | QString deviceName = device.split('@').first().trimmed(); 241 | QString outPath = uniqueTempPath(deviceName); 242 | pendingTempPath_ = outPath; 243 | 244 | // We might need extraFlags like "-y" for reading 245 | QStringList args; 246 | args << "-p" << device << "-r" << outPath; 247 | args << extraFlags; 248 | 249 | startMinipro(Mode::Reading, args); 250 | } 251 | 252 | // Write from a given file to chip 253 | void ProcessHandling::writeChipImage(const QString& programmer, 254 | const QString& device, 255 | const QString& filePath, 256 | const QStringList& extraFlags) 257 | { 258 | QStringList args; 259 | // We might need extraFlags like "-y" for writing 260 | args << "-p" << device << "-w" << filePath; 261 | args << extraFlags; 262 | 263 | startMinipro(Mode::Writing, args); 264 | } 265 | 266 | // Scan for connected programmers (minipro -k) 267 | void ProcessHandling::scanConnectedDevices() { 268 | const QStringList args{ "-k" }; 269 | 270 | startMinipro(Mode::Scan, args); 271 | } 272 | 273 | // Fetch supported devices for a given programmer (minipro -q -l) 274 | void ProcessHandling::fetchSupportedDevices(const QString &programmer) 275 | { 276 | // Supported devices need programmer name: -q -l 277 | const QStringList args{ "-q", programmer, "-l" }; 278 | 279 | startMinipro(Mode::DeviceList, args); 280 | } 281 | 282 | // Fetch chip info for a given programmer and device (minipro -q -d "") 283 | void ProcessHandling::fetchChipInfo(const QString &programmer, const QString &device) 284 | { 285 | // Stupid fix for QComboBox emitting signal twice 286 | if (mode_ == Mode::ChipInfo) return; 287 | 288 | // Chip info needs programmer and device: -q -d 289 | QStringList args; 290 | if (!programmer.isEmpty()) 291 | args << "-q" << programmer; 292 | args << "-d" << device; 293 | 294 | startMinipro(Mode::ChipInfo, args); 295 | } 296 | 297 | // Check if chip is blank: minipro -p -b 298 | void ProcessHandling::checkIfBlank(const QString &programmer, 299 | const QString &device, 300 | const QStringList &extraFlags) 301 | { 302 | QStringList args; 303 | args << "-p" << device << "-b"; 304 | args << extraFlags; 305 | 306 | startMinipro(Mode::Generic, args); 307 | } 308 | 309 | // Erase chip: minipro -p -E 310 | void ProcessHandling::eraseChip(const QString &programmer, 311 | const QString &device, 312 | const QStringList &extraFlags) 313 | { 314 | QStringList args; 315 | args << "-p" << device << "-E"; 316 | args << extraFlags; 317 | 318 | startMinipro(Mode::Generic, args); 319 | } 320 | 321 | // Test logic chip: minipro -p -T 322 | void ProcessHandling::testLogicChip(const QString &programmer, 323 | const QString &device, 324 | const QStringList &extraFlags) 325 | { 326 | QStringList args; 327 | args << "-p" << device << "-T"; 328 | args << extraFlags; 329 | 330 | startMinipro(Mode::Logic, args); 331 | } 332 | 333 | // Send input to the running process (for prompts). 334 | // not used yet. 335 | void ProcessHandling::sendResponse(const QString &input) { 336 | if (process_.state() == QProcess::Running) { 337 | QTextStream(&process_).operator<<(input + "\n"); 338 | process_.waitForBytesWritten(100); 339 | } 340 | } 341 | 342 | // Helpers for parsing progress and stripping ANSI/VT codes 343 | QString ProcessHandling::stripAnsi(QString s) { 344 | static QRegularExpression ansiRe(R"(\x1B\[[0-9;]*[A-Za-z])"); 345 | return s.remove(ansiRe); 346 | } 347 | 348 | // Extract percentage from a line of text, or -1 if none found, 349 | // for progress bar updates. Handles "xx%", "xx %", and "… OK" endings. 350 | // OK is treated as 100%. 351 | int ProcessHandling::extractPercent(const QString &line) { 352 | static const QRegularExpression okTail( 353 | R"((?:ms|sec)?\s*ok\s*$|verification\s*ok\s*$)", 354 | QRegularExpression::CaseInsensitiveOption); 355 | if (okTail.match(line).hasMatch()) 356 | return 100; 357 | 358 | static QRegularExpression re(R"((\d{1,3})\s*%)"); 359 | auto m = re.match(line); 360 | if (!m.hasMatch()) return -1; 361 | 362 | bool ok = false; 363 | int pct = m.captured(1).toInt(&ok); 364 | return (ok && pct >= 0 && pct <= 100) ? pct : -1; 365 | } 366 | 367 | // Detect phase text from a line, e.g. "Reading" or "Writing" 368 | // for progress bar text updates. Returns empty string if none found. 369 | QString ProcessHandling::detectPhaseText(const QString &line) { 370 | static const QRegularExpression rd(R"(\bReading\s*Code\.\.\.)", 371 | QRegularExpression::CaseInsensitiveOption); 372 | static const QRegularExpression wr(R"(\bWriting\s*Code\.\.\.)", 373 | QRegularExpression::CaseInsensitiveOption); 374 | 375 | if (rd.match(line).hasMatch()) return QStringLiteral("Reading"); 376 | if (wr.match(line).hasMatch()) return QStringLiteral("Writing"); 377 | return {}; 378 | } 379 | 380 | // Handle stdout+stderr (merged) from the process, parse progress and log lines. 381 | // Parses lines for errors/warnings and emits logLine() or errorLine() as needed. 382 | // Calls extractPercent() and detectPhaseText() to parse progress bar updates. 383 | void ProcessHandling::processOutputLine(QString ln) { 384 | if (ln.isEmpty()) return; 385 | 386 | if (mode_ == Mode::Logic) { 387 | ln = stripAnsi(ln); 388 | } else { 389 | // Remove ANSI sequences for non-logic modes 390 | ln = stripAnsi(ln).trimmed(); 391 | } 392 | 393 | if (ln.isEmpty()) return; 394 | 395 | stdoutBuffer_.append(ln + "\n"); 396 | 397 | // We want to log only specific output 398 | if (ln.contains("error", Qt::CaseInsensitive)) { 399 | emit errorLine(ln); 400 | } else if (ln.contains("warning", Qt::CaseInsensitive)) { 401 | if (!ln.contains("not yet complete", Qt::CaseInsensitive)) // ignore "not yet completed" warnings 402 | emit logLine(ln); 403 | } else if (ln.contains("invalid", Qt::CaseInsensitive)) { 404 | emit errorLine(ln); 405 | } else if (ln.contains("incorrect", Qt::CaseInsensitive)) { 406 | emit errorLine(ln); 407 | } else if (ln.contains("failed", Qt::CaseInsensitive)) { 408 | emit errorLine(ln); 409 | } else if (ln.contains("can't", Qt::CaseInsensitive)) { 410 | emit errorLine(ln); 411 | } else if (ln.contains("is blank", Qt::CaseInsensitive)) { 412 | emit logLine(ln); 413 | } else if (ln.contains("success", Qt::CaseInsensitive)) { 414 | emit logLine(ln); 415 | } else if (ln.endsWith(" ok", Qt::CaseInsensitive)) { 416 | emit logLine(ln); 417 | } else if (mode_ == Mode::Logic) { 418 | emit logLine(ln); 419 | } 420 | 421 | // Parse possible progress from stdout too 422 | const int pct = extractPercent(ln); 423 | const QString phase = detectPhaseText(ln); 424 | if (pct >= 0 && pct <= 100) { 425 | emit progress(pct, phase); 426 | } 427 | } 428 | 429 | // Adds ANSI-stripped lines to internal stdoutBuffer_ for later parsing by 430 | // the handleFinished() slot. 431 | void ProcessHandling::handleStdout() { 432 | const QByteArray raw = process_.readAllStandardOutput(); 433 | if (raw.isEmpty() && stdoutFragment_.isEmpty()) return; 434 | 435 | QString chunk = QString::fromLocal8Bit(raw); 436 | chunk.replace("\r\n", "\n"); 437 | chunk.replace('\r', '\n'); 438 | stdoutFragment_.append(chunk); 439 | 440 | int newlineIndex = -1; 441 | while ((newlineIndex = stdoutFragment_.indexOf('\n')) != -1) { 442 | QString line = stdoutFragment_.left(newlineIndex); 443 | stdoutFragment_.remove(0, newlineIndex + 1); 444 | processOutputLine(line); 445 | } 446 | } 447 | 448 | // Process has finished, parse final output based on mode and 449 | // emit appropriate signals. 450 | void ProcessHandling::handleFinished(int exitCode, QProcess::ExitStatus status) { 451 | // Drain any remaining output that might not have triggered readyRead. 452 | handleStdout(); 453 | if (!stdoutFragment_.isEmpty()) { 454 | processOutputLine(stdoutFragment_); 455 | stdoutFragment_.clear(); 456 | } 457 | 458 | // Scanning for devices 459 | if (mode_ == Mode::Scan) { 460 | const QStringList names = parseProgrammerList(stdoutBuffer_); 461 | mode_ = Mode::Idle; 462 | emit devicesScanned(names); 463 | // Get list of supported devices 464 | } else if (mode_ == Mode::DeviceList) { 465 | QStringList devices = stdoutBuffer_.split('\n', Qt::SkipEmptyParts); 466 | for (QString &s : devices) { 467 | s = s.trimmed(); 468 | } 469 | // very light filtering of device list, for empty lines, removing duplicates etc. 470 | devices.erase(std::remove_if(devices.begin(), devices.end(), [](const QString &s){ 471 | return s.isEmpty(); 472 | }), devices.end()); 473 | devices.removeDuplicates(); 474 | mode_ = Mode::Idle; 475 | emit devicesListed(devices); 476 | // Get single chip info 477 | } else if (mode_ == Mode::ChipInfo) { 478 | const ChipInfo ci = parseChipInfo(stdoutBuffer_); 479 | mode_ = Mode::Idle; 480 | emit chipInfoReady(ci); 481 | // Logic chip test 482 | } else if (mode_ == Mode::Reading) { 483 | const bool ok = (status == QProcess::NormalExit && exitCode == 0); 484 | const QString tempPath = pendingTempPath_; 485 | pendingTempPath_.clear(); 486 | if (ok) { 487 | mode_ = Mode::Idle; 488 | emit readReady(tempPath); 489 | } else { 490 | if (!tempPath.isEmpty()) QFile::remove(tempPath); 491 | mode_ = Mode::Idle; 492 | emit errorLine(QString("[Read error] exit=%1").arg(exitCode)); 493 | } 494 | // Chip programming 495 | } else if (mode_ == Mode::Writing) { 496 | const bool ok = (status == QProcess::NormalExit && exitCode == 0); 497 | if (ok) { 498 | mode_ = Mode::Idle; 499 | emit writeDone(); 500 | } else { 501 | mode_ = Mode::Idle; 502 | emit errorLine(QString("[Write error] exit=%1").arg(exitCode)); 503 | } 504 | } else { 505 | mode_ = Mode::Idle; 506 | } 507 | 508 | // All operations eventually end up here, send finished() signal to 509 | // release the UI. 510 | stdoutBuffer_.clear(); 511 | emit finished(exitCode, status); 512 | } 513 | -------------------------------------------------------------------------------- /thirdparty/LICENSE_libusb: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | 504 | 505 | --------------------------------------------------------------------------------