├── 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 | 
14 | 
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 |
--------------------------------------------------------------------------------