changeset 5:115cfa4a3239 default tip

Added icon upload function for OSTC 4/5 For the upload the same process as the one for the firmware update is used => CRC functionality has been copied from the ostc_pack SW
author Ideenmodellierer
date Tue, 30 Dec 2025 21:41:02 +0100
parents e30f00f760d3
children
files CmakeLists.txt HardwareOperations.h MainWindow.cpp OSTC3Operations.cpp OSTC4Operations.cpp OSTC4Operations.h about.txt crc_wrapper.cpp crc_wrapper.h crcmodel.c crcmodel.h ostc45_icon.cpp ostc45_icon.h
diffstat 13 files changed, 811 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/CmakeLists.txt	Sun Nov 30 18:37:32 2025 +0100
+++ b/CmakeLists.txt	Tue Dec 30 21:41:02 2025 +0100
@@ -1,8 +1,6 @@
 cmake_minimum_required(VERSION 3.21)
 
-project(OSTCCompanion VERSION 1.0 LANGUAGES CXX)
-
-project(OSTCCompanion LANGUAGES CXX)
+project(OSTCCompanion VERSION 1.0 LANGUAGES CXX C)
 
 # ----------------------------------------
 # C++ Standard
@@ -79,6 +77,7 @@
     OSTC4Operations.h
     OSTC_CR_Operations.h
     editlogdialog.h
+    crcmodel.h
 )
 
 set(UIS
@@ -102,6 +101,17 @@
 
 set(app_icon_resource_windows "${CMAKE_CURRENT_SOURCE_DIR}/icon.rc")
 
+
+add_library(crcmodel STATIC
+    crcmodel.c
+)
+set_source_files_properties(crcmodel.c
+    PROPERTIES
+        LANGUAGE C
+)
+target_include_directories(crcmodel PUBLIC
+    ${CMAKE_CURRENT_SOURCE_DIR}
+)
 # ----------------------------------------
 # Executable erstellen
 # ----------------------------------------
@@ -112,6 +122,10 @@
     ${RESOURCES}
     Utils/Export.h
     ${app_icon_resource_windows}
+    ostc45_icon.cpp
+    ostc45_icon.h
+    crc_wrapper.h
+    crc_wrapper.cpp
 )
 
 target_include_directories(${PROJECT_NAME} PRIVATE
@@ -124,7 +138,7 @@
 # Qt Libraries verlinken
 # ----------------------------------------
 target_link_libraries(${PROJECT_NAME}
-    PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Svg Qt6::Xml Qt6::SerialPort
+    PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Svg Qt6::Xml Qt6::SerialPort crcmodel
 )
 
 # ----------------------------------------
--- a/HardwareOperations.h	Sun Nov 30 18:37:32 2025 +0100
+++ b/HardwareOperations.h	Tue Dec 30 21:41:02 2025 +0100
@@ -166,7 +166,7 @@
     /// Each driver (instance of this class) is requested to tell *Companion*
     /// what are the supported command.
     ///
-    enum CompanionFeatures {
+    enum CompanionFeature {
         PARAMETERS   = (1<<0),  ///< Download/Edit/Upload various parameters.
         DATE         = (1<<1),  ///< Set date & time.
         NAME         = (1<<2),  ///< Set custom text displayed on main screen.
@@ -179,7 +179,7 @@
         VPM_MODEL    = (1<<9),  ///< Also propose VPM deco stops.
         SIGNAL_CHECK = (1<<10)  ///< Echo signal quality for bluetooth computer
     };
-
+    Q_DECLARE_FLAGS(CompanionFeatures, CompanionFeature)
     /// \brief Tells what is supported for a given computer.
     virtual CompanionFeatures supported() const = 0;
 
--- a/MainWindow.cpp	Sun Nov 30 18:37:32 2025 +0100
+++ b/MainWindow.cpp	Tue Dec 30 21:41:02 2025 +0100
@@ -294,15 +294,15 @@
 
     // ON when supported but NOT connected, OFF once connected.
     _ui->upgradeButton->setEnabled( ok
-                                && (_op->supported() & HardwareOperations::FIRMWARE)
+                                   && (_op->supported() & HardwareOperations::CompanionFeatures(HardwareOperations::FIRMWARE))
                                 && !_op->serial().isOpen());
 
     // Only allow buttons when connected:
     ok &= _op->serial().isOpen();
-    _ui->dateButton   ->setEnabled( ok && (_op->supported() & HardwareOperations::DATE) );
-    _ui->nameButton   ->setEnabled( ok && (_op->supported() & HardwareOperations::NAME) );
-    _ui->iconButton   ->setEnabled( ok && (_op->supported() & HardwareOperations::ICON) );
-    _ui->signalButton ->setEnabled( ok && (_op->supported() & HardwareOperations::SIGNAL_CHECK) );
+    _ui->dateButton  ->setEnabled(ok && _op->supported().testFlag(HardwareOperations::DATE));
+    _ui->nameButton  ->setEnabled(ok && _op->supported().testFlag(HardwareOperations::NAME));
+    _ui->iconButton  ->setEnabled(ok);// && _op->supported().testFlag(HardwareOperations::ICON));
+    _ui->signalButton->setEnabled(ok && _op->supported().testFlag(HardwareOperations::SIGNAL_CHECK));
     _ui->closeButton  ->setEnabled( ok );
 }
 
@@ -411,13 +411,8 @@
         QString fileName = QFileDialog::getOpenFileName(this,
                                                         "Icon File...",
                                                         QString(),
-                                                        "Images (*.gif *.png *.tif *.tiff *.jpg *.jpeg *.ico *.svg);;"
-                                                        "GIF Image (*.gif);;"
-                                                        "PNG Image (*.png);;"
-                                                        "TIFF Image (*.tif *.tiff);;"
-                                                        "JPEG Image (*.jpg *.jpeg);;"
-                                                        "Icon Image (*.ico);;"
-                                                        "SVG Image (*.svg);;"
+                                                        "Images (*.bmp);;"
+                                                        "BMP Image (*.bmp);;"
                                                         "Anything (*.*)");
         if( ! fileName.isEmpty() )
             _op->setIcons(fileName);
--- a/OSTC3Operations.cpp	Sun Nov 30 18:37:32 2025 +0100
+++ b/OSTC3Operations.cpp	Tue Dec 30 21:41:02 2025 +0100
@@ -497,9 +497,12 @@
     //       Also, the general acknowledge byte is changed to 0x4c 'L'.
 
     assert( _connectMode != SERVICE_MODE );
-    _serial.open( Settings::port, emulatorName);
-    _serial.sleep(333); // Initial 1/3 sec before trying service mode...
 
+    if( _connectMode == CLOSED_MODE )
+    {
+        _serial.open( Settings::port, emulatorName);
+        _serial.sleep(333); // Initial 1/3 sec before trying service mode...
+    }
     for(int retry=0; retry < 10; ++retry)
     {
         unsigned char serviceMode[] = { 0xAA, 0xAB, 0xCD, 0xEF };
--- a/OSTC4Operations.cpp	Sun Nov 30 18:37:32 2025 +0100
+++ b/OSTC4Operations.cpp	Tue Dec 30 21:41:02 2025 +0100
@@ -38,6 +38,7 @@
 #include <QRegularExpression>
 #include "OSTC4Operations.h"
 
+
 #include "Utils/Log.h"
 #include "Utils/Exception.h"
 #include "Utils/ProgressEvent.h"
@@ -64,9 +65,21 @@
 
 HardwareOperations::CompanionFeatures OSTC4Operations::supported() const
 {
-    // No ICON, no DUMPSCREEN
-    return CompanionFeatures(PARAMETERS|DATE|NAME|FIRMWARE
-                     |HELIUM_DIVE|CCR_DIVE|BLUETOOTH|VPM_MODEL|SIGNAL_CHECK);
+    // No DUMPSCREEN
+
+    CompanionFeatures features;
+    features |= PARAMETERS;
+    features |= DATE;
+    features |= NAME;
+    features |= FIRMWARE;
+    features |= HELIUM_DIVE;
+    features |= CCR_DIVE;
+    features |= BLUETOOTH;
+    features |= VPM_MODEL;
+    features |= SIGNAL_CHECK;
+    features |= ICON;
+
+   return features;
 }
 
 QString OSTC4Operations::firmwareTemplate() const
@@ -188,6 +201,7 @@
   : fileChecksum(0,0)
 {
     emulatorName = "OSTC4/5";
+    m_crcWrapper = new CrcWrapper(nullptr);
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -202,18 +216,131 @@
     LOG_INFO("Please wait for OSTC4/5 to complete installation...");
 }
 
+#define SWAP4BYTES(x)  \
+uint( (((x) & 0x000000FF) << 24)  \
+     | (((x) & 0x0000FF00) <<  8)  \
+     | (((x) & 0x00FF0000) >>  8)  \
+     | (((x) & 0xFF000000) >> 24))
+
+void OSTC4Operations::setIcons(const QString &fileName)
+{
+    int size;
+    int blockSize;
+    int transferDelay;
+    unsigned char echo;
+
+    FirmwareOSTC4 header;
+
+    unsigned char buf3offset[4];
+    unsigned char bufVersion[4];
+    uint32_t checksum = 0;
+    uint32_t width = 0;
+    uint32_t height = 0;
+
+    blockSize = FIRMWARE_BLOCK;
+    transferDelay = FIRMWARE_BLOCK_DELAY;
+
+    try
+    {
+        bmp = std::make_unique<BmpToArray>(fileName);
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "Fehler beim Laden: " << e.what() << '\n';
+    }
+
+    QByteArray bin = bmp->getTransferBytes();
+    bmp->getImageXY(&width, &height);
+
+    if(bin.size() < 200000)
+    {
+        buf3offset[0] = 0x20;
+        buf3offset[1] = 0x00;
+        buf3offset[2] = 0x00;
+        buf3offset[3] = 0x00;
+        bufVersion[0] = 1;
+        bufVersion[1] = 0;
+        bufVersion[2] = 0;
+        bufVersion[3] = 0;
+
+        header.type = 0x20;
+        /* original header shallnot be modifieded => split image size information into 5 bytes */
+        header.dummy5 = width;          /* low byte */
+        header.dummy6 = height;         /* low byte */
+        header.dummy7 = (width >> 8) & 0x0000000F;  /* shifted high nibble */
+        header.dummy7 |= (height >> 4) & 0x000000F0; /* shifted high nibble */
+
+        header.length = SWAP4BYTES(bin.size());
+        header.checksum = bin.size() + (256*256*256*header.type) + (256*256* header.dummy5) + (256*header.dummy6) + header.dummy7;
+
+        checksum = SWAP4BYTES(m_crcWrapper->CRC_CalcBlockCRC(reinterpret_cast<uint32_t*>(bin.data()), bin.size() / 4));
+
+        //---- Upload icon -----------------------------------------------
+        const int steps = 3 + (bin.size()+blockSize-1) / blockSize;
+        int step = 0;
+        PROGRESS(step++, steps);
+
+        LOG_INFO(QString("    uploading icon size %1 bytes").arg(bin.size()));
+        writeText( QString("Upload Icon") );
+
+        //---- Command
+        PROGRESS(step++, steps);
+        echo = retryCommand(_serial, 'o'); // 0x6F upload icon
+        if( echo != 'o' )
+            LOG_THROW("Bad OSTC4/5 icon upload.");
+
+        //---- Header
+        PROGRESS(step++, steps);
+        _serial.writeBlock((uchar*)&header, sizeof header);
+        _serial.sleep(500);
+        //---- Data
+        for(int len = 0x00000; len < bin.size(); len += blockSize)
+        {
+            if(transferDelay)
+            {
+                _serial.sleep(transferDelay);  // 15 msec between 64B blocks...
+            }
+            PROGRESS(step++, steps);
+
+            size = qMin(blockSize,
+                        bin.size() - len);
+
+            LOG_TRACE("Fill " << size);
+            _serial.writeBlock((uchar*)bin.data()+len, (unsigned int)size);
+        }
+        _serial.writeBlock((uchar*)&checksum, 4);
+        PROGRESS(steps, steps); // 100%
+
+        //---- Wait acknowledge ----------------------------------------------
+        PROGRESS_THROTTLE();
+
+            for(int wait=0; wait<2*60; ++wait)  // Up to 2 min...
+            {
+                try {
+                    unsigned char reply = _serial.readByte();
+                    if( reply == 0x4D )
+                        break;
+                }
+                catch( const ReadTimeout&) {}
+            }
+
+        LOG_INFO("    icon uploaded.");
+    }
+    else
+    {
+        LOG_THROW("Image to large. Must be < 200k.");
+    }
+    PROGRESS_RESET();
+}
+
+
+
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 ///     FIRMWARE UPGRADE                                                   ///
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 
-#define SWAP4BYTES(x)  \
-    uint( (((x) & 0x000000FF) << 24)  \
-        | (((x) & 0x0000FF00) <<  8)  \
-        | (((x) & 0x00FF0000) >>  8)  \
-        | (((x) & 0xFF000000) >> 24))
-
 //////////////////////////////////////////////////////////////////////////////
 
 void OSTC4Operations::openFirmware(const QString &fileName, bool dryRun)
--- a/OSTC4Operations.h	Sun Nov 30 18:37:32 2025 +0100
+++ b/OSTC4Operations.h	Tue Dec 30 21:41:02 2025 +0100
@@ -45,8 +45,11 @@
 #include <QRegularExpression>
 
 #include "AES/Adler16.h"
+#include "ostc45_icon.h"
+#include "crc_wrapper.h"
 #include "Export.h"
 #include <QFile>
+#include <QString>
 
 //////////////////////////////////////////////////////////////////////////////
 /// \brief  Implementing various operations for H&W OSTC4 dive computer
@@ -79,6 +82,8 @@
 
     QFile   _file;
 
+    std::unique_ptr<BmpToArray> bmp;
+
     /// \param[in] dryRun: If TRUE, do not upload FW, just check file structure.
     void openFirmware(const QString& fileName, bool dryRun);
 
@@ -91,6 +96,7 @@
     void writeAllHeader(unsigned char* pBuffer) override;
     void getAllSamples(unsigned char* pBuffer) override;
     void writeAllSamples(unsigned char* pBuffer) override;
+    void setIcons(const QString& fileName) override;
 
     EXPORT QString firmwareTemplate() const override;
    // EXPORT QRegExp portTemplate() const override;
@@ -100,6 +106,8 @@
 
     Adler32 fileChecksum;
 
+    CrcWrapper* m_crcWrapper;
+
 public:
     EXPORT OSTC4Operations();
 };
--- a/about.txt	Sun Nov 30 18:37:32 2025 +0100
+++ b/about.txt	Tue Dec 30 21:41:02 2025 +0100
@@ -1,1 +1,1 @@
-This repository holds the source code for the OSTC Companion program.
\ No newline at end of file
+This repository holds the source code for the OSTC Companion program.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crc_wrapper.cpp	Tue Dec 30 21:41:02 2025 +0100
@@ -0,0 +1,77 @@
+#include "crc_wrapper.h"
+
+extern "C" {
+#include "crcmodel.h"
+}
+
+
+CrcWrapper::CrcWrapper(QObject *parent)
+    : QObject(parent)
+{}
+
+
+void CrcWrapper::init(p_cm_t p_cm)
+{
+   cm_ini(p_cm);
+}
+
+void CrcWrapper::cm_next(p_cm_t p_cm, int ch)
+{
+    cm_nxt(p_cm, ch);
+}
+
+uint32_t CrcWrapper::CRC_CalcBlockCRC(uint32_t *buffer, uint32_t words)
+{
+    cm_t        crc_model;
+    uint32_t      word_to_do;
+    uint8_t       byte_to_do;
+    int         i;
+
+    // Values for the STM32F generator.
+
+    crc_model.cm_width = 32;            // 32-bit CRC
+    crc_model.cm_poly  = 0x04C11DB7;    // CRC-32 polynomial
+    crc_model.cm_init  = 0xFFFFFFFF;    // CRC initialized to 1's
+    crc_model.cm_refin = FALSE;         // CRC calculated MSB first
+    crc_model.cm_refot = FALSE;         // Final result is not bit-reversed
+    crc_model.cm_xorot = 0x00000000;    // Final result XOR'ed with this
+
+    cm_ini(&crc_model);
+
+    while (words--)
+    {
+        // The STM32F10x hardware does 32-bit words at a time!!!
+
+        word_to_do = *buffer++;
+
+        // Do all bytes in the 32-bit word.
+
+        for (i = 0; i < sizeof(word_to_do); i++)
+        {
+            // We calculate a *byte* at a time. If the CRC is MSB first we
+            // do the next MS byte and vica-versa.
+
+            if (crc_model.cm_refin == FALSE)
+            {
+                // MSB first. Do the next MS byte.
+
+                byte_to_do = (uint8_t) ((word_to_do & 0xFF000000) >> 24);
+                word_to_do <<= 8;
+            }
+            else
+            {
+                // LSB first. Do the next LS byte.
+
+                byte_to_do = (uint8_t) (word_to_do & 0x000000FF);
+                word_to_do >>= 8;
+            }
+
+            cm_nxt(&crc_model, byte_to_do);
+        }
+    }
+
+    // Return the final result.
+
+    return (cm_crc(&crc_model));
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crc_wrapper.h	Tue Dec 30 21:41:02 2025 +0100
@@ -0,0 +1,23 @@
+#ifndef CRC_WRAPPER_H
+#define CRC_WRAPPER_H
+
+extern "C" {
+#include "crcmodel.h"
+}
+#include <QObject>
+
+class CrcWrapper : public QObject
+{
+    Q_OBJECT
+public:
+    explicit CrcWrapper(QObject *parent = nullptr);
+
+    void init(p_cm_t p_cm);
+    void cm_next(p_cm_t p_cm,int ch);
+
+    uint32_t CRC_CalcBlockCRC(uint32_t *buffer, uint32_t words);
+
+
+};
+
+#endif // CRC_WRAPPER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crcmodel.c	Tue Dec 30 21:41:02 2025 +0100
@@ -0,0 +1,137 @@
+/******************************************************************************/
+/*                             Start of crcmodel.c                            */
+/******************************************************************************/
+/*                                                                            */
+/* Author : Ross Williams (ross@guest.adelaide.edu.au.).                      */
+/* Date   : 3 June 1993.                                                      */
+/* Status : Public domain.                                                    */
+/*                                                                            */
+/* Description : This is the implementation (.c) file for the reference       */
+/* implementation of the Rocksoft^tm Model CRC Algorithm. For more            */
+/* information on the Rocksoft^tm Model CRC Algorithm, see the document       */
+/* titled "A Painless Guide to CRC Error Detection Algorithms" by Ross        */
+/* Williams (ross@guest.adelaide.edu.au.). This document is likely to be in   */
+/* "ftp.adelaide.edu.au/pub/rocksoft".                                        */
+/*                                                                            */
+/* Note: Rocksoft is a trademark of Rocksoft Pty Ltd, Adelaide, Australia.    */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/* Implementation Notes                                                       */
+/* --------------------                                                       */
+/* To avoid inconsistencies, the specification of each function is not echoed */
+/* here. See the header file for a description of these functions.            */
+/* This package is light on checking because I want to keep it short and      */
+/* simple and portable (i.e. it would be too messy to distribute my entire    */
+/* C culture (e.g. assertions package) with this package.                     */
+/*                                                                            */
+/******************************************************************************/
+
+#include "crcmodel.h"
+
+/******************************************************************************/
+
+/* The following definitions make the code more readable. */
+
+#define BITMASK(X) (1L << (X))
+#define MASK32 0xFFFFFFFFL
+#define LOCAL static
+
+/******************************************************************************/
+
+LOCAL ulong reflect P_((ulong v,int b));
+LOCAL ulong reflect (ulong v,int b)
+/* Returns the value v with the bottom b [0,32] bits reflected. */
+/* Example: reflect(0x3e23L,3) == 0x3e26                        */
+{
+ int   i;
+ ulong t = v;
+ for (i=0; i<b; i++)
+   {
+    if (t & 1L)
+       v|=  BITMASK((b-1)-i);
+    else
+       v&= ~BITMASK((b-1)-i);
+    t>>=1;
+   }
+ return v;
+}
+
+/******************************************************************************/
+
+LOCAL ulong widmask P_((p_cm_t));
+LOCAL ulong widmask (p_cm_t p_cm)
+/* Returns a longword whose value is (2^p_cm->cm_width)-1.     */
+/* The trick is to do this portably (e.g. without doing <<32). */
+{
+ return (((1L<<(p_cm->cm_width-1))-1L)<<1)|1L;
+}
+
+/******************************************************************************/
+
+void cm_ini (p_cm_t p_cm)
+{
+ p_cm->cm_reg = p_cm->cm_init;
+}
+
+/******************************************************************************/
+
+void cm_nxt (p_cm_t p_cm, int ch)
+{
+ int   i;
+ ulong uch  = (ulong) ch;
+ ulong topbit = BITMASK(p_cm->cm_width-1);
+
+ if (p_cm->cm_refin) uch = reflect(uch,8);
+ p_cm->cm_reg ^= (uch << (p_cm->cm_width-8));
+ for (i=0; i<8; i++)
+   {
+    if (p_cm->cm_reg & topbit)
+       p_cm->cm_reg = (p_cm->cm_reg << 1) ^ p_cm->cm_poly;
+    else
+       p_cm->cm_reg <<= 1;
+    p_cm->cm_reg &= widmask(p_cm);
+   }
+}
+
+/******************************************************************************/
+
+void cm_blk (p_cm_t p_cm,p_ubyte_ blk_adr,ulong blk_len)
+{
+ while (blk_len--) cm_nxt(p_cm,*blk_adr++);
+}
+
+/******************************************************************************/
+
+ulong cm_crc (p_cm_t p_cm)
+{
+ if (p_cm->cm_refot)
+    return p_cm->cm_xorot ^ reflect(p_cm->cm_reg,p_cm->cm_width);
+ else
+    return p_cm->cm_xorot ^ p_cm->cm_reg;
+}
+
+/******************************************************************************/
+
+ulong cm_tab (p_cm_t p_cm, int index)
+{
+ int   i;
+ ulong r;
+ ulong topbit = BITMASK(p_cm->cm_width-1);
+ ulong inbyte = (ulong) index;
+
+ if (p_cm->cm_refin) inbyte = reflect(inbyte,8);
+ r = inbyte << (p_cm->cm_width-8);
+ for (i=0; i<8; i++)
+    if (r & topbit)
+       r = (r << 1) ^ p_cm->cm_poly;
+    else
+       r<<=1;
+ if (p_cm->cm_refin) r = reflect(r,p_cm->cm_width);
+ return r & widmask(p_cm);
+}
+
+/******************************************************************************/
+/*                             End of crcmodel.c                              */
+/******************************************************************************/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crcmodel.h	Tue Dec 30 21:41:02 2025 +0100
@@ -0,0 +1,197 @@
+///////////////////////////////////////////////////////////////////////////////
+/// -*- coding: UTF-8 -*-
+///
+/// \file   Discovery/Inc/crcmodel.h
+/// \brief  Rocksoft CRC Model Algorithm
+/// \author Ross Williams (ross@guest.adelaide.edu.au.) and Heinrichs Weikamp
+/// \date   3 June 1993
+///
+/// \details
+///
+///     This is the header (.h) file for the reference
+///     implementation of the Rocksoft^tm Model CRC Algorithm. For more
+///     information on the Rocksoft^tm Model CRC Algorithm, see the document
+///     titled "A Painless Guide to CRC Error Detection Algorithms" by Ross
+///     Williams (ross@guest.adelaide.edu.au.). This document is likely to be in
+///     "ftp.adelaide.edu.au/pub/rocksoft".
+///
+///     Note: Rocksoft is a trademark of Rocksoft Pty Ltd, Adelaide, Australia.
+///
+/// $Id$
+///////////////////////////////////////////////////////////////////////////////
+/// \par Copyright (c) 2014-2018 Heinrichs Weikamp gmbh
+///
+///     This program is free software: you can redistribute it and/or modify
+///     it under the terms of the GNU General Public License as published by
+///     the Free Software Foundation, either version 3 of the License, or
+///     (at your option) any later version.
+///
+///     This program is distributed in the hope that it will be useful,
+///     but WITHOUT ANY WARRANTY; without even the implied warranty of
+///     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+///     GNU General Public License for more details.
+///
+///     You should have received a copy of the GNU General Public License
+///     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//////////////////////////////////////////////////////////////////////////////
+
+/******************************************************************************/
+/*                             Start of crcmodel.h                            */
+/******************************************************************************/
+/*                                                                            */
+/* Author : Ross Williams (ross@guest.adelaide.edu.au.).                      */
+/* Date   : 3 June 1993.                                                      */
+/* Status : Public domain.                                                    */
+/*                                                                            */
+/* Description : This is the header (.h) file for the reference               */
+/* implementation of the Rocksoft^tm Model CRC Algorithm. For more            */
+/* information on the Rocksoft^tm Model CRC Algorithm, see the document       */
+/* titled "A Painless Guide to CRC Error Detection Algorithms" by Ross        */
+/* Williams (ross@guest.adelaide.edu.au.). This document is likely to be in   */
+/* "ftp.adelaide.edu.au/pub/rocksoft".                                        */
+/*                                                                            */
+/* Note: Rocksoft is a trademark of Rocksoft Pty Ltd, Adelaide, Australia.    */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/* How to Use This Package                                                    */
+/* -----------------------                                                    */
+/* Step 1: Declare a variable of type cm_t. Declare another variable          */
+/*         (p_cm say) of type p_cm_t and initialize it to point to the first  */
+/*         variable (e.g. p_cm_t p_cm = &cm_t).                               */
+/*                                                                            */
+/* Step 2: Assign values to the parameter fields of the structure.            */
+/*         If you don't know what to assign, see the document cited earlier.  */
+/*         For example:                                                       */
+/*            p_cm->cm_width = 16;                                            */
+/*            p_cm->cm_poly  = 0x8005L;                                       */
+/*            p_cm->cm_init  = 0L;                                            */
+/*            p_cm->cm_refin = TRUE;                                          */
+/*            p_cm->cm_refot = TRUE;                                          */
+/*            p_cm->cm_xorot = 0L;                                            */
+/*         Note: Poly is specified without its top bit (18005 becomes 8005).  */
+/*         Note: Width is one bit less than the raw poly width.               */
+/*                                                                            */
+/* Step 3: Initialize the instance with a call cm_ini(p_cm);                  */
+/*                                                                            */
+/* Step 4: Process zero or more message bytes by placing zero or more         */
+/*         successive calls to cm_nxt. Example: cm_nxt(p_cm,ch);              */
+/*                                                                            */
+/* Step 5: Extract the CRC value at any time by calling crc = cm_crc(p_cm);   */
+/*         If the CRC is a 16-bit value, it will be in the bottom 16 bits.    */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/* Design Notes                                                               */
+/* ------------                                                               */
+/* PORTABILITY: This package has been coded very conservatively so that       */
+/* it will run on as many machines as possible. For example, all external     */
+/* identifiers have been restricted to 6 characters and all internal ones to  */
+/* 8 characters. The prefix cm (for Crc Model) is used as an attempt to avoid */
+/* namespace collisions. This package is endian independent.                  */
+/*                                                                            */
+/* EFFICIENCY: This package (and its interface) is not designed for           */
+/* speed. The purpose of this package is to act as a well-defined reference   */
+/* model for the specification of CRC algorithms. If you want speed, cook up  */
+/* a specific table-driven implementation as described in the document cited  */
+/* above. This package is designed for validation only; if you have found or  */
+/* implemented a CRC algorithm and wish to describe it as a set of parameters */
+/* to the Rocksoft^tm Model CRC Algorithm, your CRC algorithm implementation  */
+/* should behave identically to this package under those parameters.          */
+/*                                                                            */
+/******************************************************************************/
+
+
+
+#ifndef CRC_MODEL_H
+#define CRC_MODEL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/******************************************************************************/
+
+/* The following definitions are extracted from my style header file which    */
+/* would be cumbersome to distribute with this package. The DONE_STYLE is the */
+/* idempotence symbol used in my style header file.                           */
+
+#ifndef DONE_STYLE
+
+#include <stdbool.h>
+
+typedef unsigned long   ulong;
+typedef unsigned char * p_ubyte_;
+
+#ifndef TRUE
+#define FALSE 0
+#define TRUE  1
+#endif
+
+/* Change to the second definition if you don't have prototypes. */
+#define P_(A) A
+/* #define P_(A) () */
+
+/* Uncomment this definition if you don't have void. */
+/* typedef int void; */
+
+#endif
+
+/******************************************************************************/
+
+/* CRC Model Abstract Type */
+/* ----------------------- */
+/* The following type stores the context of an executing instance of the  */
+/* model algorithm. Most of the fields are model parameters which must be */
+/* set before the first initializing call to cm_ini.                      */
+typedef struct
+	{
+	 int   cm_width;   /* Parameter: Width in bits [8,32].       */
+	 ulong cm_poly;    /* Parameter: The algorithm's polynomial. */
+	 ulong cm_init;    /* Parameter: Initial register value.     */
+	 bool  cm_refin;   /* Parameter: Reflect input bytes?        */
+	 bool  cm_refot;   /* Parameter: Reflect output CRC?         */
+	 ulong cm_xorot;   /* Parameter: XOR this to output CRC.     */
+
+	 ulong cm_reg;     /* Context: Context during execution.     */
+	} cm_t;
+typedef cm_t *p_cm_t;
+
+/******************************************************************************/
+
+/* Functions That Implement The Model */
+/* ---------------------------------- */
+/* The following functions animate the cm_t abstraction. */
+
+void cm_ini P_((p_cm_t p_cm));
+/* Initializes the argument CRC model instance.          */
+/* All parameter fields must be set before calling this. */
+
+void cm_nxt P_((p_cm_t p_cm,int ch));
+/* Processes a single message byte [0,255]. */
+
+void cm_blk P_((p_cm_t p_cm,p_ubyte_ blk_adr,ulong blk_len));
+/* Processes a block of message bytes. */
+
+ulong cm_crc P_((p_cm_t p_cm));
+/* Returns the CRC value for the message bytes processed so far. */
+
+/******************************************************************************/
+
+/* Functions For Table Calculation */
+/* ------------------------------- */
+/* The following function can be used to calculate a CRC lookup table.        */
+/* It can also be used at run-time to create or check static tables.          */
+
+ulong cm_tab P_((p_cm_t p_cm,int index));
+/* Returns the i'th entry for the lookup table for the specified algorithm.   */
+/* The function examines the fields cm_width, cm_poly, cm_refin, and the      */
+/* argument table index in the range [0,255] and returns the table entry in   */
+/* the bottom cm_width bytes of the return value.                             */
+
+/******************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+#endif // CRC_MODEL_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ostc45_icon.cpp	Tue Dec 30 21:41:02 2025 +0100
@@ -0,0 +1,163 @@
+#include "ostc45_icon.h"
+
+
+//OSTC45_Icon::OSTC45_Icon() {}
+
+
+#pragma pack(push, 1)
+struct BMPFileHeader
+{
+    uint16_t bfType;
+    uint32_t bfSize;
+    uint16_t bfReserved1;
+    uint16_t bfReserved2;
+    uint32_t bfOffBits;
+};
+
+struct BMPInfoHeader
+{
+    uint32_t biSize;
+    int32_t  biWidth;
+    int32_t  biHeight;
+    uint16_t biPlanes;
+    uint16_t biBitCount;
+    uint32_t biCompression;
+    uint32_t biSizeImage;
+    int32_t  biXPelsPerMeter;
+    int32_t  biYPelsPerMeter;
+    uint32_t biClrUsed;
+    uint32_t biClrImportant;
+};
+#pragma pack(pop)
+
+BmpToArray::BmpToArray(const QString& filename)
+{
+    loadBMP(filename);
+}
+
+void BmpToArray::loadBMP(const QString& filename)
+{
+    QFile file(filename);
+    int dstY;
+    QByteArray rowBuffer;
+    bool topDown = false;
+    BMPFileHeader fileHeader;
+    BMPInfoHeader infoHeader;
+
+    if (!file.open(QIODevice::ReadOnly))
+        throw std::runtime_error("Cannot open BMP file");
+
+    if (file.read(reinterpret_cast<char*>(&fileHeader),
+                  sizeof(BMPFileHeader)) != sizeof(BMPFileHeader))
+        throw std::runtime_error("Failed to read BMP file header");
+
+    if (file.read(reinterpret_cast<char*>(&infoHeader),
+                  sizeof(BMPInfoHeader)) != sizeof(BMPInfoHeader))
+        throw std::runtime_error("Failed to read BMP info header");
+
+    if (fileHeader.bfType != 0x4D42)
+        throw std::runtime_error("Not a valid BMP file");
+
+    if (infoHeader.biBitCount != 8)
+        throw std::runtime_error("Only 8-bit BMP supported");
+    if (infoHeader.biCompression != 0)
+        throw std::runtime_error("Compressed BMP not supported");
+
+    if(infoHeader.biWidth > 800)
+        throw std::runtime_error("Only BMP with 800 or less horizontal pixels supported");
+
+    if(infoHeader.biHeight > 480)
+        throw std::runtime_error("Only BMP with 480 or less vertical pixels supported");
+
+    // Width / Height
+    width = infoHeader.biWidth;
+    height = infoHeader.biHeight;
+
+    if ((int32_t)height < 0) {
+        height = -((int32_t)height);
+        topDown = true;
+    }
+
+    // Palette
+    uint32_t colorCount = infoHeader.biClrUsed;
+    if (colorCount == 0) colorCount = 256;
+    if (colorCount > 256) colorCount = 256;
+
+    clut.resize(colorCount);
+
+    for (uint32_t i = 0; i < colorCount; ++i)
+        file.read(reinterpret_cast<char*>(&clut[i]), 4);
+
+    // CLUT in 32-Bit transform 0x00RRGGBB
+    clut32.resize(255, 0);
+
+    for (uint32_t i = 0; i < colorCount && i < 255; ++i)
+    {
+        clut32[i] =
+            (clut[i].r << 16) |
+            (clut[i].g << 8) |
+            (clut[i].b);
+    }
+
+    // Pixel-Data
+    if (!file.seek(fileHeader.bfOffBits))
+        throw std::runtime_error("Failed to seek to pixel data");
+
+    pixelData.resize(width * height);
+
+    size_t rowSize = (width + 3) & ~3; // 4-Byte alignment
+
+    rowBuffer.resize(rowSize);
+
+    for (int y = 0; y < height; ++y)
+    {
+        if (file.read(rowBuffer.data(), rowSize) != rowSize)
+            throw std::runtime_error("Failed to read BMP pixel row");
+
+        if (topDown)
+        {
+            dstY = height - 1 - y;
+        }
+        else
+        {
+            dstY = y;
+        }
+
+        for (int x = 0; x < width; ++x)
+        {
+            pixelData[x * height + dstY] =
+                static_cast<uint8_t>(rowBuffer[x]);
+        }
+    }
+
+
+}
+QByteArray BmpToArray::getTransferBytes() const
+{
+    QByteArray out;
+
+    out.reserve(clut32.size() * 4 + pixelData.size());
+
+    // CLUT (32 Bit, Little Endian)
+    for (uint32_t color : clut32)
+    {
+        out.append(static_cast<char>( color        & 0xFF));
+        out.append(static_cast<char>((color >> 8)  & 0xFF));
+        out.append(static_cast<char>((color >> 16) & 0xFF));
+        out.append(static_cast<char>((color >> 24) & 0xFF));
+    }
+
+    // Pixel (8 Bit)
+    out.append(reinterpret_cast<const char*>(pixelData.data()),
+               pixelData.size());
+
+    return out;
+}
+
+void BmpToArray::getImageXY(uint32_t* x, uint32_t* y)
+{
+    *x = width;
+    *y = height;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ostc45_icon.h	Tue Dec 30 21:41:02 2025 +0100
@@ -0,0 +1,37 @@
+#ifndef OSTC45_ICON_H
+#define OSTC45_ICON_H
+
+#include <cstdint>
+#include <vector>
+#include <stdexcept>
+#include <iostream>
+#include <QString>
+#include <QVector>
+#include <QIODevice>
+#include <QFile>
+#include <QByteArray>
+
+class BmpToArray
+{
+public:
+    struct CLUTEntry {
+        uint8_t b;
+        uint8_t g;
+        uint8_t r;
+        uint8_t a; // BMP setzt meist 0
+    };
+
+    QByteArray getTransferBytes() const;
+    void getImageXY(uint32_t* x, uint32_t* y);
+    BmpToArray(const QString& fileName);
+
+private:
+    void loadBMP(const QString& filename);
+
+    QVector<CLUTEntry> clut;
+    QVector<uint8_t> pixelData;     // Pixel-Indizes
+    QVector<uint32_t> clut32;       // 32-Bit Palette
+    uint32_t width = 0;
+    uint32_t height = 0;
+};
+#endif // OSTC45_ICON_H