Mercurial > public > ostc_companion
changeset 1:0b3630a29ad8
Initial version based on previous repository.
Project was ported to QT6 and in now cmake based.
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AES/Adler16.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,110 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file Adler16.h +/// \brief Adler checksum, on 2x8 bits. +/// \author JD Gascuel. +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2015-07-31 Creation. +// 2016-05-24 jDG: BSD-2 version. + +#ifndef ADLER16_H +#define ADLER16_H + +#include <QByteArray> + +////////////////////////////////////////////////////////////////////////////// +/// \brief Addler checksum, on 2xN bits. +template <typename T> +class AdlerTemplate +{ + T _a, _b; + +public: + //------------------------------------------------------------------------ + /// \brief Initialize checksum with a given seed. + inline AdlerTemplate(T a, T b) + : _a(a), _b(b) + {} + + //------------------------------------------------------------------------ + /// \brief Re-initialize checksum with a given seed. + inline void reset(T a, T b) + { _a = a; + _b = b; + } + + //------------------------------------------------------------------------ + /// \brief Hash a single byte/word + inline void add(int c) + { + _a += (T)(c & 0xFF); + _b += _a; + } + + //------------------------------------------------------------------------ + /// \brief Hash a block of bytes. + /// \note \a size is always in BYTE units. + inline void add(const void* buffer, size_t size) + { + for(size_t i = 0; i<size; ++i) + add( ((const unsigned char*)buffer)[i] ); + } + + //------------------------------------------------------------------------ + /// \brief Hash all the byte/words for a Qt buffer. + inline void add(const QByteArray& buffer) + { + add(buffer.data(), buffer.size()); + } + + //------------------------------------------------------------------------ + /// \brief Returns hash result. + inline T a() const { return _a; } + inline T b() const { return _b; } + + //------------------------------------------------------------------------ + /// Check Hash result. + /// \returns TRUE if has do match the given values. + inline bool check(T a, T b) + { + return (_a == a) && (_b == b); + } +}; + +/// \brief 2x8 bits implementation. +typedef AdlerTemplate<unsigned char> Adler16; + +/// \brief 2x16 bits implementation. +typedef AdlerTemplate<unsigned short> Adler32; + +#endif // ADDLER16_H +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AES/rijndael.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,1285 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file rijndael.cpp +/// \brief Public Domain AES encryption/decryption +/// \author Philip J. Erdelsky <pje@efgh.com>, JD Gascuel, and others. +/// +/// \copyright (c) 2015 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "rijndael.h" + +#ifdef WIN32 +# include <Windows.h> // GetTickCount64() +#else +# include <sys/time.h> // gettimeofday() +#endif + +namespace Rijndael { + +#define FULL_UNROLL + +static const Word32 Te0[256] = +{ + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; + +static const Word32 Te1[256] = +{ + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; + +static const Word32 Te2[256] = +{ + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; + +static const Word32 Te3[256] = +{ + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; + +static const Word32 Te4[256] = +{ + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; + +static const Word32 Td0[256] = +{ + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; + +static const Word32 Td1[256] = +{ + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; + +static const Word32 Td2[256] = +{ + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; + +static const Word32 Td3[256] = +{ + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; + +static const Word32 Td4[256] = +{ + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; + +static const Word32 rcon[] = +{ + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, + /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#define GETU32(plaintext) (((Word32)(plaintext)[0] << 24) ^ \ + ((Word32)(plaintext)[1] << 16) ^ \ + ((Word32)(plaintext)[2] << 8) ^ \ + ((Word32)(plaintext)[3])) + +#define PUTU32(ciphertext, st) { (ciphertext)[0] = (Byte)((st) >> 24); \ + (ciphertext)[1] = (Byte)((st) >> 16); \ + (ciphertext)[2] = (Byte)((st) >> 8); \ + (ciphertext)[3] = (Byte)(st); } + +/** + * Expand the cipher key into the encryption key schedule. + * + */ +void AES::setupEncrypt(Word32 *rk, const Byte* key, int keybits) +{ + rk[0] = GETU32(key ); + rk[1] = GETU32(key + 4); + rk[2] = GETU32(key + 8); + rk[3] = GETU32(key + 12); + if (keybits == 128) + { + for (int i=0;;) + { + Word32 temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) + return; + rk += 4; + } + } + rk[4] = GETU32(key + 16); + rk[5] = GETU32(key + 20); + if (keybits == 192) + { + for (int i=0;;) + { + Word32 temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) + return; + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(key + 24); + rk[7] = GETU32(key + 28); + if (keybits == 256) + { + for (int i=0;;) + { + Word32 temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp ) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) + return; + temp = rk[11]; + rk[12] = rk[ 4] ^ + (Te4[(temp >> 24) ] & 0xff000000) ^ + (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(temp ) & 0xff] & 0x000000ff); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + rk += 8; + } + } +} + +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +void AES::setupDecrypt(Word32* rk, const Byte *key, int keybits) +{ + int i, j; + + /* expand the cipher key: */ + setupEncrypt(rk, key, keybits); + int nrounds = NROUNDS(keybits); + + /* invert the order of the round keys: */ + for (i = 0, j = 4*nrounds; i < j; i += 4, j -= 4) + { + Word32 temp; + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < nrounds; i++) + { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0] ) & 0xff] & 0xff]; + rk[1] = + Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1] ) & 0xff] & 0xff]; + rk[2] = + Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2] ) & 0xff] & 0xff]; + rk[3] = + Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3] ) & 0xff] & 0xff]; + } +} + +void AES::encrypt(Word32* rk, int nrounds, + const Block plaintext, Block ciphertext) +{ + Word32 s0, s1, s2, s3, t0, t1, t2, t3; + #ifndef FULL_UNROLL + int r; + #endif /* ?FULL_UNROLL */ + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(plaintext ) ^ rk[0]; + s1 = GETU32(plaintext + 4) ^ rk[1]; + s2 = GETU32(plaintext + 8) ^ rk[2]; + s3 = GETU32(plaintext + 12) ^ rk[3]; + #ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + if (nrounds > 10) + { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; + if (nrounds > 12) + { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += nrounds << 2; + #else /* !FULL_UNROLL */ + /* + * nrounds - 1 full rounds: + */ + r = nrounds >> 1; + for (;;) + { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + rk += 8; + if (--r == 0) + break; + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } + #endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24) ] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(ciphertext , s0); + s1 = + (Te4[(t1 >> 24) ] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(ciphertext + 4, s1); + s2 = + (Te4[(t2 >> 24) ] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(ciphertext + 8, s2); + s3 = + (Te4[(t3 >> 24) ] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(ciphertext + 12, s3); +} + +void AES::decrypt(Word32* rk, int nrounds, + const Block ciphertext, Block plaintext) +{ + Word32 s0, s1, s2, s3, t0, t1, t2, t3; + #ifndef FULL_UNROLL + int r; + #endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(ciphertext ) ^ rk[0]; + s1 = GETU32(ciphertext + 4) ^ rk[1]; + s2 = GETU32(ciphertext + 8) ^ rk[2]; + s3 = GETU32(ciphertext + 12) ^ rk[3]; + #ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + if (nrounds > 10) + { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; + if (nrounds > 12) + { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += nrounds << 2; + #else /* !FULL_UNROLL */ + /* + * nrounds - 1 full rounds: + */ + r = nrounds >> 1; + for (;;) + { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + rk += 8; + if (--r == 0) + break; + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } + #endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(plaintext , s0); + s1 = + (Td4[(t1 >> 24) ] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(plaintext + 4, s1); + s2 = + (Td4[(t2 >> 24) ] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(plaintext + 8, s2); + s3 = + (Td4[(t3 >> 24) ] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(plaintext + 12, s3); +} + +////////////////////////////////////////////////////////////////////////////// +/// Randomness idea inspired from ANSI X9.17 proposed PRNG, see wikipedia +/// PRNG page... +static Word32 CRYPT(Word32* rk, int keybits, Word32 x) +{ + // Pad the block by self repetition. + Block in; + ((Word32*)&in)[0] = x; + ((Word32*)&in)[1] = x *= 2147483647; + ((Word32*)&in)[2] = x *= 2147483647; + ((Word32*)&in)[3] = x *= 2147483647; + + Block out; + AES::encrypt(rk, NROUNDS(keybits), in, out); + return ((Word32*)&out)[3]; +} + +Word32 AES::get_random(Word32* rk, int keybits) +{ +#ifdef WIN32 + LONGLONG counter = 0; + QueryPerformanceCounter( (LARGE_INTEGER*)&counter ); + Word32 time = (Word32)(counter % 2147483647); +#else + timeval ts; + gettimeofday(&ts,0); + Word32 time = (Word32) ts.tv_usec; // microseconds. +#endif + + // X9.17 propose to use triple-DES for that... Keep it simpler with our + // ECB block stuff: + static Word32 seed = 0x61448126; // Needs to be none zero. + Word32 tmp = CRYPT(rk, keybits, time); + Word32 x = CRYPT(rk, keybits, tmp ^ seed); + seed = CRYPT(rk, keybits, tmp ^ x); + + return x; +} + +////////////////////////////////////////////////////////////////////////////// +} // namespace Rijndael
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AES/rijndael.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,296 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file rijndael.h +/// \brief Public Domain AES encryption/decryption +/// \author Philip J. Erdelsky <pje@efgh.com>, JD Gascuel, and others. +/// +/// \copyright (c) 2015 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2002-09-03 PJE: original source http://www.efgh.com/software/rijndael.htm +// 2015-03-14 jDG: Import into OSTC_Companion, major C++ rewrites to insure +// key length is not messed up. Added ECb and CFB modes. + +#ifndef RIJNDAEL_H +#define RIJNDAEL_H + +namespace Rijndael { + +#define KEYLENGTH(keybits) ((keybits)/8) +#define RKLENGTH(keybits) ((keybits)/8+28) +#define NROUNDS(keybits) ((keybits)/32+6) + +typedef unsigned long Word32; +typedef unsigned char Byte; +typedef Byte Block[16]; + +////////////////////////////////////////////////////////////////////////////// +/// \brief Public Domain AES encryption/decryption rewritten. +/// +/// The Rijndael encryption algorithm has been designed to replace the aging +/// DES algorithm. Like DES, it is a block cipher. It uses 128-bit, 192-bit +/// or 256-bit keys. This implementation encrypts 128-bit blocks. +/// (DES used 56-bit keys and 64-bit blocks.) +/// +/// The code in this package is a modified version of an implementation +/// placed in the public domain by the following persons: +/// + Vincent Rijmen vincent.rijmen@esat.kuleuven.ac.be +/// + Antoon Bosselaers antoon.bosselaers@esat.kuleuven.ac.be +/// + Paulo Barreto paulo.barreto@terra.com.br +/// +/// See details in http://www.efgh.com/software/rijndael.htm +/// +struct AES +{ + static void setupEncrypt(Word32* rk, const Byte *key, int keybits); + static void setupDecrypt(Word32* rk, const Byte *key, int keybits); + static void encrypt(Word32 *rk, int nrounds, const Block plaintext, Block ciphertext); + static void decrypt(Word32 *rk, int nrounds, const Block ciphertext, Block plaintext); + static Word32 get_random(Word32 *rk, int keybits); +}; + +////////////////////////////////////////////////////////////////////////////// +template<int keybits> +class ECB : private AES +{ + /// \brief Internal state + /// + /// storage for encryption buffer, required space: + /// keybits 32-bit words required + /// 128 44 + /// 192 52 + /// 256 60 + /// \sa RKLENGTH macro. + Word32 rk[RKLENGTH(keybits)]; + +public: + typedef Byte Key[KEYLENGTH(keybits)]; + + ECB(); + + //------------------------------------------------------------------------ + /// \brief Initialize encryption state + /// + /// + /// \param[in] key: AES key, where length is: + /// keybits number of bytes + /// 128 16 + /// 192 24 + /// 256 32 + /// \sa KEYLENGTH macro. + /// + /// \code + /// Rijndael::ECB<128>::Key key = "my big secret"; + /// Rijndael::ECB<128> enc; + /// enc.setupEncrypt(key); + /// ... + /// Rijndael::Block plain = "Hello World!"; + /// Rijndael::Block result; + /// enc.encrypt(plain, result); + /// \endcode + void setupEncrypt(const Key key); + + //------------------------------------------------------------------------ + /// \brief Encrypt a block of 16 bytes. + /// + /// \param[in] plaintext: The 16 bytes block to encrypt. + /// \param[out] ciphertext: Space to store the 16 bytes of encrypted data. + /// + void encrypt(const Block plaintext, Block ciphertext); + + //------------------------------------------------------------------------ + /// \brief Initialize decryption state. + /// + /// \param[in] key: AES key, where length is: + /// keybits number of bytes + /// 128 16 + /// 192 24 + /// 256 32 + /// \sa KEYLENGTH macro. + /// + /// \code + /// Rijndael::ECB<128>::Key key = "my big secret"; + /// Rijndael::ECB<128> dec; + /// dec.setupDecrypt(key); + /// ... + /// Rijndael::Block cipher = ...; + /// Rijndael::Block result; + /// Rijndael::decrypt(cipher, result); + /// \endcode + void setupDecrypt(const Key key); + + //------------------------------------------------------------------------ + /// \brief Decrypt a block of 16 bytes. + /// + /// \param[in] ciphertext: The 16 bytes block of data to decrypt. + /// \param[out] plaintext: Space to store the 16 bytes result block. + /// + void decrypt(const Block ciphertext, Block plaintext); + + //---------------------------------------------------------------------------- + /// \brief Crypto base PRNG + /// + /// Based on wall-clock value and current key, but should be a crypto-secure + /// generator. + Word32 get_random(); +}; + +////////////////////////////////////////////////////////////////////////////// +template<int keybits> +class CFB : public ECB<keybits> +{ + /// Initialization vector (salt), updated in CFB mode + /// for the next block of text. + Block iv; + +public: + typedef Byte Key[KEYLENGTH(keybits)]; + typedef Block IV; + + //------------------------------------------------------------------------ + /// \brief Initialize encryption/decription state + /// + /// + /// \param[in] key: AES key, where length is: + /// keybits number of bytes + /// 128 16 + /// 192 24 + /// 256 32 + /// \sa KEYLENGTH macro. + /// \param[in] iv: initialization vector. Some randomness needed to + /// enforce the sequence is non replayable. + /// + /// \code + /// Rijndael::CFB<128>::Key key = "my big secret"; + /// Rijndael::CFB<128> enc(key, iv); + /// ... + /// Rijndael::Block plain = "Hello World!"; + /// Rijndael::Block cipher, again; + /// enc.encrypt(plain, cipher); + /// enc.decrypt(cipher, again); + /// \endcode + CFB(const Key key, const IV iv); + + //------------------------------------------------------------------------ + /// \brief Encrypt a block of 16 bytes, with IV (CFB mode) + /// + /// \param[in] plaintext: The 16 bytes block to encrypt. + /// \param[out] ciphertext: Space to store the 16 bytes of encrypted data. + /// + void encrypt(const Block plaintext, Block ciphertext); + + //---------------------------------------------------------------------------- + /// \brief Decrypt a block of 16 bytes, with IV (CFB mode) + /// + /// \param[in] ciphertext: The 16 bytes block of data to decrypt. + /// \param[out] plaintext: Space to store the 16 bytes result block. + /// + void decrypt(const Block ciphertext, Block plaintext); +}; + +////////////////////////////////////////////////////////////////////////////// + +template<int keybits> +ECB<keybits>::ECB() +{ + for(int i=0; i<RKLENGTH(keybits); ++i) + rk[i] = 0; +} + +template<int keybits> +void ECB<keybits>::setupEncrypt(const Key key) +{ + AES::setupEncrypt(rk, key, keybits); +} + +template<int keybits> +void ECB<keybits>::encrypt(const Block plaintext, Block ciphertext) +{ + AES::encrypt(rk, NROUNDS(keybits), plaintext, ciphertext); +} + +////////////////////////////////////////////////////////////////////////////// + +template<int keybits> +void ECB<keybits>::setupDecrypt(const Key key) +{ + AES::setupDecrypt(rk, key, keybits); +} + +template<int keybits> +void ECB<keybits>::decrypt(const Block ciphertext, Block plaintext) +{ + AES::decrypt(rk, NROUNDS(keybits), ciphertext, plaintext); +} + +////////////////////////////////////////////////////////////////////////////// + +template<int keybits> +Word32 ECB<keybits>::get_random() +{ + return AES::get_random(rk, keybits); +} + +////////////////////////////////////////////////////////////////////////////// + +template<int keybits> +CFB<keybits>::CFB(const Key key, const IV _iv) +{ + ECB<keybits>::setupEncrypt(key); + for(int i=0; i<16; ++i) + iv[i] = _iv[i]; +} + +template<int keybits> +void CFB<keybits>::encrypt(const Block plaintext, Block ciphertext) +{ + Block tmp; + ECB<keybits>::encrypt(iv, tmp); + for(int i=0; i<16; ++i) + { + ciphertext[i] = plaintext[i] ^ tmp[i]; + iv[i] = ciphertext[i]; + } +} + +template<int keybits> +void CFB<keybits>::decrypt(const Block ciphertext, Block plaintext) +{ + Block tmp; + ECB<keybits>::encrypt(iv, tmp); + for(int i=0; i<16; ++i) + { + plaintext[i] = ciphertext[i] ^ tmp[i]; + iv[i] = ciphertext[i]; + } +} + +} +#endif // RIJNDAEL_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AES/unit_test.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,215 @@ +//////////////////////////////////////////////////////////////////////// +/// \file AES/unit_test.cpp +/// \brief Test AES block mode, and CFB mode. +/// \author (c) jD Gascuel +/// +/// $Id$ +//////////////////////////////////////////////////////////////////////// +// File History: +// 2015-03-14 [jDG] Creation. + +#include "Rijndael.h" +#include "gtest/gtest.h" + +#include <set> + +/// Vectors from http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors#aes-ecb-128 + +static const Rijndael::Block plain[4] = { + {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}, + {0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51}, + {0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef}, + {0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10} +}; + +static const Rijndael::ECB<128>::Key key128 = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c +}; + +static const Rijndael::Block cipher128[4] = { + {0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97}, + {0xf5, 0xd3, 0xd5, 0x85, 0x03, 0xb9, 0x69, 0x9d, 0xe7, 0x85, 0x89, 0x5a, 0x96, 0xfd, 0xba, 0xaf}, + {0x43, 0xb1, 0xcd, 0x7f, 0x59, 0x8e, 0xce, 0x23, 0x88, 0x1b, 0x00, 0xe3, 0xed, 0x03, 0x06, 0x88}, + {0x7b, 0x0c, 0x78, 0x5e, 0x27, 0xe8, 0xad, 0x3f, 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4} +}; + +static const Rijndael::ECB<256>::Key key256 = { + 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 +}; + +static const Rijndael::Block cipher256[4] = { + {0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8}, + {0x59, 0x1c, 0xcb, 0x10, 0xd4, 0x10, 0xed, 0x26, 0xdc, 0x5b, 0xa7, 0x4a, 0x31, 0x36, 0x28, 0x70}, + {0xb6, 0xed, 0x21, 0xb9, 0x9c, 0xa6, 0xf4, 0xf9, 0xf1, 0x53, 0xe7, 0xb1, 0xbe, 0xaf, 0xed, 0x1d}, + {0x23, 0x30, 0x4b, 0x7a, 0x39, 0xf9, 0xf3, 0xff, 0x06, 0x7d, 0x8d, 0x8f, 0x9e, 0x24, 0xec, 0xc7} +}; + +////////////////////////////////////////////////////////////////////////////// + +static const Rijndael::Block IV[4] = { + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, + {0x3B, 0x3F, 0xD9, 0x2E, 0xB7, 0x2D, 0xAD, 0x20, 0x33, 0x34, 0x49, 0xF8, 0xE8, 0x3C, 0xFB, 0x4A}, + {0xC8, 0xA6, 0x45, 0x37, 0xA0, 0xB3, 0xA9, 0x3F, 0xCD, 0xE3, 0xCD, 0xAD, 0x9F, 0x1C, 0xE5, 0x8B}, + {0x26, 0x75, 0x1F, 0x67, 0xA3, 0xCB, 0xB1, 0x40, 0xB1, 0x80, 0x8C, 0xF1, 0x87, 0xA4, 0xF4, 0xDF} +}; + +static const Rijndael::Block cfb_cipher128[4] = { + {0x3b, 0x3f, 0xd9, 0x2e, 0xb7, 0x2d, 0xad, 0x20, 0x33, 0x34, 0x49, 0xf8, 0xe8, 0x3c, 0xfb, 0x4a}, + {0xc8, 0xa6, 0x45, 0x37, 0xa0, 0xb3, 0xa9, 0x3f, 0xcd, 0xe3, 0xcd, 0xad, 0x9f, 0x1c, 0xe5, 0x8b}, + {0x26, 0x75, 0x1f, 0x67, 0xa3, 0xcb, 0xb1, 0x40, 0xb1, 0x80, 0x8c, 0xf1, 0x87, 0xa4, 0xf4, 0xdf}, + {0xc0, 0x4b, 0x05, 0x35, 0x7c, 0x5d, 0x1c, 0x0e, 0xea, 0xc4, 0xc6, 0x6f, 0x9f, 0xf7, 0xf2, 0xe6} +}; + +////////////////////////////////////////////////////////////////////////////// + +TEST(Rijndael, ECB_PRNG) { + Rijndael::ECB<128> enc; + enc.setupEncrypt(key128); + + const int samples = 100*1000; + std::set<Rijndael::Word32> dict; + + int collisions = 0; + for(int i=0; i<samples; ++i) { + Rijndael::Word32 x = enc.get_random(); + + // Should not hit the zero value: + ASSERT_NE(0, x); + + // Should scarcely produce the same value twice + if(dict.find(x) != dict.end()) + ++collisions; + else + dict.insert(x); + } + // Observed: 250e-6 + ASSERT_LE(float(collisions)/float(samples), 500e-6f); +} + +////////////////////////////////////////////////////////////////////////////// + +TEST(Rijndael, ECB_encrypt_128) { + Rijndael::ECB<128> enc; + enc.setupEncrypt(key128); + + for(int test=0; test<4; ++test) { + Rijndael::Block result = {0}; + enc.encrypt(plain[test], result); + for(int c=0; c<16; ++c) + EXPECT_EQ(cipher128[test][c], result[c]) + << "Test " << test << ": byte " << c; + } +} + +////////////////////////////////////////////////////////////////////////////// + +TEST(Rijndael, ECB_decrypt_128) { + Rijndael::ECB<128> dec; + dec.setupDecrypt(key128); + + for(int test=0; test<4; ++test) { + Rijndael::Block result = {0}; + dec.decrypt(cipher128[test], result); + for(int c=0; c<16; ++c) + EXPECT_EQ(plain[test][c], result[c]) + << "Test " << test << ": byte " << c; + } +} + +////////////////////////////////////////////////////////////////////////////// + +TEST(Rijndael, ECB_encrypt_256) { + Rijndael::ECB<256> enc; + enc.setupEncrypt(key256); + + for(int test=0; test<4; ++test) { + Rijndael::Block result = {0}; + enc.encrypt(plain[test], result); + for(int c=0; c<16; ++c) + ASSERT_EQ(cipher256[test][c], result[c]) + << "Test " << test << ": byte " << c; + } +} + +////////////////////////////////////////////////////////////////////////////// + +TEST(Rijndael, ECB_decrypt_256) { + Rijndael::ECB<256> dec; + dec.setupDecrypt(key256); + + for(int test=0; test<4; ++test) { + Rijndael::Block result = {0}; + dec.decrypt(cipher256[test], result); + for(int c=0; c<16; ++c) + ASSERT_EQ(plain[test][c], result[c]) + << "Test " << test << ": byte " << c; + } +} + +////////////////////////////////////////////////////////////////////////////// + +TEST(Rijndael, CFB_encrypt_128) { + for(int test=0; test<4; ++test) { + Rijndael::CFB<128> enc(key128, IV[test]); + + Rijndael::Block result = {0}; + enc.encrypt(plain[test], result); + for(int c=0; c<16; ++c) + { + ASSERT_EQ(cfb_cipher128[test][c], result[c]) + << "Test " << test << ": byte " << c; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +TEST(Rijndael, CFB_decrypt_128) { + for(int test=0; test<4; ++test) { + Rijndael::CFB<128> dec(key128, IV[test]); + + Rijndael::Block result = {0}; + dec.decrypt(cfb_cipher128[test], result); + for(int c=0; c<16; ++c) + { + ASSERT_EQ(plain[test][c], result[c]) + << "Test " << test << ": byte " << c; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +TEST(Rijndael, CFB_encrypt_128_chained) { + Rijndael::CFB<128> enc(key128, IV[0]); + + for(int test=0; test<4; ++test) { + Rijndael::Block result = {0}; + enc.encrypt(plain[test], result); + for(int c=0; c<16; ++c) + { + ASSERT_EQ(cfb_cipher128[test][c], result[c]) + << "Test " << test << ": byte " << c; + } + } +} + + +////////////////////////////////////////////////////////////////////////////// + +TEST(Rijndael, CFB_decrypt_128_chained) { + Rijndael::CFB<128> dec(key128, IV[0]); + + for(int test=0; test<4; ++test) { + Rijndael::Block result = {0}; + dec.decrypt(cfb_cipher128[test], result); + for(int c=0; c<16; ++c) + { + ASSERT_EQ(plain[test][c], result[c]) + << "Test " << test << ": byte " << c; + } + } +} + +//////////////////////////////////////////////////////////////////////////////
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AES/unit_test.pro Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,36 @@ +############################################################################## +# \file AES/unit_test.pro +# \brief Test AES block mode, and CFB mode. +# \author JD Gascuel +# +# $Id$ +############################################################################## +# HISTORY: +# 2015/03/14 jDG : Creation. + +TARGET = unit_test +CONFIG *= console + +############################################################################## +# Here, we need dependencies from GoogleTest, and another project... +GTEST=D:/Dev/Dependencies/gtest-1.6.0 + +# Visual Studio 2012 : the number of variadic template parameters defaults to 5, +# force to 10 +*msvc2012 : DEFINES *= _VARIADIC_MAX=10 + +INCLUDEPATH *= $${GTEST}/include $${GTEST}/gtest-1.6.0 +HEADERS *= \ + $${GTEST}/include/gtest/gtest.h +SOURCES *= \ + $${GTEST}/gtest-1.6.0/src/gtest_main.cc \ + $${GTEST}/gtest-1.6.0/src/gtest-all.cc + +############################################################################## +# Then we have our proper stuff: +HEADERS *= \ + rijndael.h + +SOURCES *= \ + rijndael.cpp \ + unit_test.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CmakeLists.txt Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,234 @@ +cmake_minimum_required(VERSION 3.21) + +project(OSTCCompanion VERSION 1.0 LANGUAGES CXX) + +project(OSTCCompanion LANGUAGES CXX) + +# ---------------------------------------- +# C++ Standard +# ---------------------------------------- +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +# ---------------------------------------- +# Qt6 Modules +# ---------------------------------------- +find_package(Qt6 REQUIRED COMPONENTS + Core + Gui + Widgets + Network + Svg + Xml + SerialPort + LinguistTools +) + + +# ---------------------------------------- +# Sources, Header, UI and Ressources +# ---------------------------------------- +set(SOURCES + main.cpp + AES/rijndael.cpp + editlogdialog.cpp + Utils/Exception.cpp + Utils/Log.cpp + Utils/LogAppender.cpp + Utils/LogConsole.cpp + Utils/LogFile.cpp + Utils/ProgressEvent.cpp + MainWindow.cpp + Serial.cpp + HexFile.cpp + SettingsDialog.cpp + HardwareOperations.cpp + OSTCFrogOperations.cpp + OSTCSportOperations.cpp + OSTC2cOperations.cpp + OSTC2Operations.cpp + OSTC3Operations.cpp + OSTC3pOperations.cpp + OSTC4Operations.cpp + OSTC_CR_Operations.cpp +) + +set(HEADERS + AES/rijndael.h + AES/Adler16.h + Utils/Exception.h + Utils/Log.h + Utils/LogAppender.h + Utils/LogConsole.h + Utils/LogFile.h + Utils/ProgressEvent.h + MainWindow.h + Serial.h + HexFile.h + SettingsDialog.h + HardwareOperations.h + OSTCFrogOperations.h + OSTCSportOperations.h + OSTC2cOperations.h + OSTC2Operations.h + OSTC3Operations.h + OSTC3pOperations.h + OSTC4Operations.h + OSTC_CR_Operations.h + editlogdialog.h + ui_LogEditor.h +) + +set(UIS + LogEditor.ui + MainWindow.ui + Settings.ui +) + +set(RESOURCES + Resources.qrc + icon.rc +) + +set(TRANSLATIONS + Translations/companion_DE.ts + Translations/companion_FR.ts + Translations/companion_ES.ts + Translations/companion_IT.ts + Translations/companion_RU.ts +) + +set(app_icon_resource_windows "${CMAKE_CURRENT_SOURCE_DIR}/icon.rc") + +# ---------------------------------------- +# Executable erstellen +# ---------------------------------------- +qt_add_executable(${PROJECT_NAME} + ${SOURCES} + ${HEADERS} + ${UIS} + ${RESOURCES} + Utils/Export.h + ${app_icon_resource_windows} +) + +target_include_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} # Root + ${CMAKE_CURRENT_SOURCE_DIR}/Utils + ${CMAKE_CURRENT_SOURCE_DIR}/AES +) + +# ---------------------------------------- +# Qt Libraries verlinken +# ---------------------------------------- +target_link_libraries(${PROJECT_NAME} + PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Svg Qt6::Xml Qt6::SerialPort +) + +# ---------------------------------------- +# Plattform-spezifische Anpassungen +# ---------------------------------------- +if(WIN32) + target_compile_definitions(${PROJECT_NAME} PRIVATE _CRT_SECURE_NO_WARNINGS=1 BUILDING_OSTC_COMPANION) +elseif(APPLE) + target_compile_options(${PROJECT_NAME} PRIVATE -std=c++11 -Wno-inconsistent-missing-override) +elseif(UNIX) + target_compile_options(${PROJECT_NAME} PRIVATE -std=c++11) + set_target_properties(${PROJECT_NAME} PROPERTIES + BUILD_RPATH "\$ORIGIN/lib" + ) +endif() + +# ---------------------------------------- +# Debug/Release-Builds +# ---------------------------------------- +if(CMAKE_BUILD_TYPE MATCHES Debug) + target_compile_definitions(${PROJECT_NAME} PRIVATE DEBUG=1) +endif() + +# ---------------------------------------- +# Version aus Datei einlesen (optional) +# ---------------------------------------- +file(STRINGS "${CMAKE_SOURCE_DIR}/VERSION.txt" VERSION_CONTENT) +string(REGEX MATCH "MAJOR=([0-9]+)" _ ${VERSION_CONTENT}) +set(MAJOR_VERSION ${CMAKE_MATCH_1}) +string(REGEX MATCH "MINOR=([0-9]+)" _ ${VERSION_CONTENT}) +set(MINOR_VERSION ${CMAKE_MATCH_1}) +string(REGEX MATCH "PATCH=([0-9]+)" _ ${VERSION_CONTENT}) +set(PATCH_VERSION ${CMAKE_MATCH_1}) +string(REGEX MATCH "BETA=([0-9]+)" _ ${VERSION_CONTENT}) +set(BETA_VERSION ${CMAKE_MATCH_1}) + + +message("MAJOR_VERSION = '${MAJOR_VERSION}'") + +target_compile_definitions(${PROJECT_NAME} PRIVATE + MAJOR_VERSION=${MAJOR_VERSION} + MINOR_VERSION=${MINOR_VERSION} + PATCH_VERSION=${PATCH_VERSION} + BETA_VERSION=${BETA_VERSION} +) + +# Show folder in Qt Creator +file(GLOB ICON_FILES "${CMAKE_SOURCE_DIR}/Images/*") +add_custom_target(Resources ALL SOURCES ${ICON_FILES}) + + +qt6_add_translation(QM_FILES ${TRANSLATIONS}) + +if(CMAKE_BUILD_TYPE MATCHES Release) + target_compile_definitions(${PROJECT_NAME} PRIVATE DEBUG=1) + + message("Build release and installer") + + set(CPACK_IFW_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/Images/inst_OSTC_Companion.ico") + + install(FILES ${CMAKE_SOURCE_DIR}/Images/app_OSTC_Companion.ico + DESTINATION . # relativ zum Package root + COMPONENT Core) + + install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin COMPONENT Core) + + qt_generate_deploy_app_script( + TARGET ${PROJECT_NAME} + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR + ) + + install(SCRIPT ${deploy_script} + COMPONENT Core) + + # CPack IFW Konfiguration + set(CPACK_GENERATOR "IFW") + set(CPACK_IFW_ROOT "D:/Programme/QT/Tools/QtInstallerFramework/4.10") + set(CPACK_PACKAGE_VERSION_MAJOR "3") + set(CPACK_PACKAGE_VERSION_MINOR "3") + set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") + set(CPACK_PACKAGE_VENDOR "Heinrichs Weikamp") + set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "OSTC Companion Application") + set (CPACK_IFW_PACKAGE_NAME "${PROJECT_NAME} 3.3.1") + set (CPACK_IFW_PACKAGE_TITLE "${PROJECT_NAME} Installer") + set (CPACK_IFW_PACKAGE_PUBLISHER "Heinrichs Weikamp") + set (CPACK_IFW_PACKAGE_WIZARD_STYLE "Modern") + set (CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST OFF) + + set (CPACK_IFW_PACKAGE_START_MENU_DIRECTORY "OSTC-Tools") + + include(CPack) + include(CPackIFW) + + # Hauptkomponente + cpack_add_component(Core + DISPLAY_NAME "OSTC Companion" + DESCRIPTION "OSTC maintenance application" + REQUIRED + ) + + cpack_ifw_configure_component(Core ESSENTIAL FORCED_INSTALLATION) +endif() + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HardwareOperations.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,377 @@ +///////////////////////////////////////////////////////////////////////////// +/// \file HardwareOperations.cpp +/// \brief Abstract operations for HW dive computers. +/// \author JD Gascuel. +/// \sa OSTC3Operations.cpp +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +/////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "HardwareOperations.h" +#include "MainWindow.h" +#include "Utils/Log.h" +#include <QSerialPortInfo> + +#ifdef WIN32 +# include <winreg.h> +#endif + +#include <QDir> + +////////////////////////////////////////////////////////////////////////////// + +unsigned char HardwareOperations::retryCommand(Serial& serial, + unsigned char cmd, + int retries) +{ + for(int retry=0; retry<retries; ++retry) + { + serial.writeByte( cmd ); // Send command + serial.sleep(25); // Allow 25msec lag. + + try { + unsigned char echo = serial.readByte(); + if( echo == cmd || echo == 'M' || echo == 'L' ) + return echo; // Got it, or unknown command... + } + catch(const ReadTimeout&) { + continue; + } + + serial.sleep(100); // Cleanup any pending stuff, + serial.purge(); // and retry... + } + + return 0xFF; +} + +////////////////////////////////////////////////////////////////////////////// + +HardwareOperations::HardwareDescriptor +HardwareOperations::hardwareDescriptor() +{ + unsigned char echo = 0; + unsigned int hardFeatures = 0xFF; // timeout response... + unsigned int softFeatures = 0; + unsigned int model = 0; + unsigned char ok = 0; + + try { + //---- First: try the new extended hardware query -------------------- + echo = retryCommand(_serial, 0x60, 1); // BACKQUOTE char + if( echo == 0x60 ) { + uchar extendedDescriptor[6]; + _serial.readBlock(extendedDescriptor, sizeof extendedDescriptor); + + + hardFeatures = extendedDescriptor[0] * 256 + extendedDescriptor[1]; + softFeatures = extendedDescriptor[2] * 256 + extendedDescriptor[3]; + model = extendedDescriptor[4]; + ok = extendedDescriptor[5]; + } + else + { + // Did we have a timeout ? + // In that case, some hwOS versions fails and need a reset of + // the connection mode... + if( echo == 0xFF ) + { + echo = retryCommand(_serial, 0xBB); // Try to reconnect + if( echo == 0xBB ) + echo = _serial.readByte(); // Eat 4d prompt + } + + // Then try the OLD hardware descriptor command... + echo = retryCommand(_serial, 'j'); // 0x6A + if( echo == 'j' ) { + hardFeatures = _serial.readByte(); + ok = _serial.readByte(); + } + } + } + catch(const ReadTimeout&) {} + + if( (echo != 0x60 && echo != 'j') + || (ok != 'M' && ok != 'L') + ) { + LOG_TRACE("Old OSTC not responding..."); + return HW_UNKNOWN_OSTC; + } + + switch(hardFeatures) { + case HW_Frog : LOG_TRACE("Frog found"); break; + case HW_OSTCSport_a : LOG_TRACE("OSTC Sport found"); break; + case HW_OSTC2c : LOG_TRACE("OSTC 2c found"); break; + case HW_OSTC2_a: + case HW_OSTC2_c : LOG_TRACE("OSTC 2 found"); break; + case HW_OSTCcR_a: + case HW_OSTCcR_b : LOG_TRACE("OSTC cR found"); break; + case HW_OSTC3 : LOG_TRACE("OSTC 3 found"); break; + case HW_OSTC3p_a : LOG_TRACE("OSTC 3+ found"); break; + case HW_OSTC4 : LOG_TRACE("OSTC 4 found"); break; + + case HW_OSTCSport_b : LOG_TRACE("OSTC Sport, OSTC 2 or OSTC 3 found."); break; + + case 0xFF: case 0x4C: case 0x4D: + LOG_TRACE("old OSTC not responding..."); + return HW_UNKNOWN_OSTC; + + default: + // LOG_TRACE("Unknown hardware feature =" << QString().sprintf("0x%04x", hardFeatures)); + LOG_TRACE("Unknown hardware feature =" << QString::asprintf("0x%04x", hardFeatures)); + break; + } + + if( echo == 0x60 ) { + LOG_TRACE(" software feature = " << QString::asprintf("0x%04x", softFeatures)); + LOG_TRACE(" model = " << QString::asprintf("0x%02x", model)); + } + + return (HardwareDescriptor)hardFeatures; +} + +////////////////////////////////////////////////////////////////////////////// + +QStringList HardwareOperations::listBluetoothPorts() const +{ + assert(supported() & BLUETOOTH); + QStringList list; + QString PortDesc; + const auto serialPortInfos = QSerialPortInfo::availablePorts(); + +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + // TODO: Linux USB search... + QRegExp pTemplate = portTemplate(); + QDir dev("/dev"); + QStringList all = dev.entryList(QStringList() << "tty.*", + QDir::NoDotAndDotDot|QDir::System|QDir::Writable, + QDir::Name); + + for(int i=0; i<(int)all.count(); ++i) { + if( pTemplate.indexIn(all[i]) >= 0 ) { + LOG_TRACE("Port " << all[i]); + list += all[i]; + } + else + LOG_DEBUG("... " << all[i]); + } +#else + /* Check the descriptors of the available COMs for Bluetooth tag */ + for (const QSerialPortInfo &serialPortInfo : serialPortInfos) { + PortDesc = serialPortInfo.description(); + if( PortDesc.contains("Bluetooth")) + list += serialPortInfo.portName(); + } + + if( list.isEmpty() ) /* no port identified => fallback to old detection function */ + { + for(int i=1; i<300; ++i) + { + QString port = QString("COM%1").arg(i); + + // First: try to read default configuration... + COMMCONFIG config = {0}; + config.dwSize = sizeof config; + config.wVersion = 1; + DWORD len = sizeof config; + + QByteArray fixed = "\\\\.\\" + port.toLocal8Bit(); + if( GetDefaultCommConfigA(fixed.constData(), &config, &len) ) { + if( config.dwProviderSubType == PST_RS232 ) + list += port; + } + } + + //---- Second chance + // overide usual MS bug, by looking into the registry for more + // BLUETOOTH ports... + { + HKEY key; + const char registryPath[] = "HARDWARE\\DEVICEMAP\\SERIALCOMM"; + if( RegOpenKeyExA(HKEY_LOCAL_MACHINE, // PWD + registryPath, // "SOFTWARE\Intel\PSIB" + 0, // Options + KEY_READ, // Desired SAM: See 32bits view. + &key) == ERROR_SUCCESS + ) { + for(DWORD i = 0; ++i;) { + char nameBuffer[128] = {0}; + DWORD nameLen = sizeof nameBuffer; + unsigned char dataBuffer[128] = {0}; + DWORD dataLen = sizeof dataBuffer; + long rc = RegEnumValueA(key, i, + nameBuffer, &nameLen, + nullptr, + nullptr, + dataBuffer, &dataLen); + if( rc != ERROR_SUCCESS ) + break; + + QString name = QString(nameBuffer); + QString port = QString((char*)dataBuffer); + LOG_TRACE("Resource " << i << ": " << name << ", " << port); + if( name.contains("\\BtModem") || name.contains("\\BthModem") || name.contains("\\BtPort") ) { + list += port + " (Bluetooth)"; + LOG_TRACE("Port " << name); + } + else + LOG_DEBUG("... " << name); + } + RegCloseKey(key); + } + } + } +#endif + + return list; +} + +////////////////////////////////////////////////////////////////////////////// + +QStringList HardwareOperations::listUSBPorts() const +{ + assert( !(supported() & BLUETOOTH) ); + QStringList list; + +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + // TODO: Linux USB search... + QDir dev("/dev"); + QRegExp pTemplate = portTemplate(); + QStringList all = dev.entryList(QStringList() << "tty.*", + QDir::System|QDir::Writable|QDir::NoDotAndDotDot, + QDir::Name); + for(int i=0; i<(int)all.count(); ++i) { + if( pTemplate.indexIn(all[i]) >= 0 ) { + LOG_TRACE("Port " << all[i]); + list += all[i]; + } + else + LOG_TRACE("... " << all[i]); + } +#else + //---- First chance: Try the normal port list: + for(int i=1; i<300; ++i) + { + QString port = QString("COM%1").arg(i); + + // First: try to read default configuration... + COMMCONFIG config; + memset(&config, 0, sizeof config); + config.dwSize = sizeof config; + config.wVersion = 1; + DWORD len = sizeof config; + + QByteArray fixed = "\\\\.\\" + port.toLocal8Bit(); + if( GetDefaultCommConfigA(fixed.constData(), &config, &len) ) { + LOG_TRACE("Port " << port << " subtype=" << int(config.dwProviderSubType) ); + if( config.dwProviderSubType == PST_RS232 ) + list += port; + } else if( len != sizeof config ) + LOG_THROW("Required " << len << " bytes."); + else if( HRESULT rc = GetLastError() ) + if( rc != 87 ) + LOG_TRACE("Port " << port << " error=" << rc ); + + } + //---- Second chance + // overide usual MS bug, by looking into the registry for more + // USB serial ports... + { + HKEY key; + const char registryPath[] = "HARDWARE\\DEVICEMAP\\SERIALCOMM"; + if( RegOpenKeyExA(HKEY_LOCAL_MACHINE, // PWD + registryPath, // path + 0, // Options + KEY_READ, // Desired SAM: See 32bits view. + &key) == ERROR_SUCCESS + ) { + for(DWORD i = 0;; ++i) { + char nameBuffer[128] = {0}; + DWORD nameLen = sizeof nameBuffer; + unsigned char dataBuffer[128] = {0}; + DWORD dataLen = sizeof dataBuffer; + long rc = RegEnumValueA(key, i, + nameBuffer, &nameLen, + nullptr, + nullptr, + dataBuffer, &dataLen); + + if( rc == ERROR_NO_MORE_ITEMS ) + break; + + if( rc != ERROR_SUCCESS ) + LOG_THROW( "Enumeration error" ); + + QString name = QString(nameBuffer); + QString port = QString((char*)dataBuffer); + LOG_TRACE("Resource " << i << ": " << name << ", " << port); + + if( name.contains("\\VCP") ) + list += port + " (USB)"; + } + RegCloseKey(key); + } + } +#endif + + return list; +} + +////////////////////////////////////////////////////////////////////////////// + +//QString HardwareOperations::scanNewPort() +//{ +// static QStringList oldPorts; + +// //---- Get current ports, type-less (ie. strip (USB) or (Bluetooth) ------ +// QStringList newPorts; +// foreach(QString port, listPorts()) +// newPorts += port.section(" ", 0, 0); + +// //---- Simplify all ports not in the list anymore ----------------------- +// for(int p=0; p<oldPorts.count(); ++p) { +// QString port = oldPorts[p]; +// if( ! newPorts.contains(port) ) +// oldPorts.removeAll(port); +// } + +// //---- Check for new port in the list ----------------------------------- +// for(int p=0; p<newPorts.count(); ++p) { +// QString port = newPorts[p]; +// // Did we find a new port ? +// if( ! oldPorts.contains(port) ) { +// oldPorts += port; +// return port; +// } +// } + +// return QString::null; +//}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HardwareOperations.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,401 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file HardwareOperations.h +/// \brief API for HW dive computer drivers. +/// \author JD Gascuel. +/// \sa OSTC3Operations.h +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2012-09-22 : [jDG] Initial version, made for OSTC Companion. +// 2014-07-07 : [jDG] Cleanups for Subsurface google-summer-of-code. +// 2014-07-25 : [jDG] BSD 2-clause license. +// 2016-07-14 : ÍjDG] Adding codes for hardware variants of OSTC2/3/4. + +#ifndef HARDWARE_OPERATIONS_H +#define HARDWARE_OPERATIONS_H + +#include <QDateTime> +#include <QImage> +#include <QString> +#include <QStringList> +#include <QSize> +#include <QRegularExpression> + +#ifdef TEST_MODE +# include "test/SerialTest.h" +#else +# include "Serial.h" +#endif + +class HexFile; + +////////////////////////////////////////////////////////////////////////////// +/// \brief API for HW dive computer drivers. +/// +/// This class include high level commands used by the *Companion* GUI, +/// and other generic services: +/// - List of currently known serial ports: +/// listBluetoothPorts() or listUSBPorts(). +/// +/// - Send a command, and wait for the proper acknowledge: retryCommand(). +/// +/// \sa OSTCFrogOperations, OSTC2cOperations, OSTC3Operations, +/// \nosubgrouping +class HardwareOperations +{ +protected: +#ifdef TEST_MODE + /// \brief Fake communication port used for mockup operations. + SerialTest _serial; +#else + /// \brief Communication port used for all I/O operations. + /// + /// Note that in *emulation mode*, aka TEST_MODE, this is replaced by + /// an instance of SerialTest. + Serial _serial; +#endif + +public: + ////////////////////////////////////////////////////////////////////////// + /// \name Class management + /// \{ + /// + /// Managing the hierarchy of driver classes used to handle the various + /// existing H&W devices. + + /// \brief mandatory (and empty) virtual descructor in the base class. + virtual ~HardwareOperations() {} + + //------------------------------------------------------------------------ + /// \brief The name of the computer driver. + /// \returns the name of a particular driver implementation, + /// eg. "OSTCSport", "OSTC2c", "OSTC3", ... + virtual QString model() const = 0; + + //------------------------------------------------------------------------ + /// \brief Gives access to serial port in use. + /// Used for other high and low level operations, eg. in *OSTC Planner*. + inline Serial& serial() { return _serial; } + + /// \} + ////////////////////////////////////////////////////////////////////////// + /// \name Introspection + /// \{ + /// + /// Methods available to ask the connected device what it does support, + /// and how to manage it. + + //------------------------------------------------------------------------ + /// \brief Optional features present in the dive computer hardware. + /// + /// 8 bit mask. See HardwareDescriptor for known combinations. + /// + enum HardwareOption { + HW_CHARGEABLE_BATTERY= 0x01, ///< Recharge option. + HW_LIGHT_SENSOR = 0x02, ///< Detects light level, and tune screen. + HW_S8_COM = 0x04, ///< Analog connector to O2 cells. + HW_OPTICAL_COM = 0x08, ///< Digital connector to O2 cells. + HW_BLUETOOTH_COM = 0x10, ///< Bluetooth, hence no USB connection. + HW_DUALCORE = 0x20, ///< Dual core processor. + }; + + ///----------------------------------------------------------------------- + /// \brief Dive computer set of features. + /// + /// Set of features present on a given H&W dive computers. + /// \note that several versions exists of a given computer, and there + /// is not a uniq mapping of a given feature set to a dive computer + /// (eg. 0x13 is ambiguous). + /// + enum HardwareDescriptor { + HW_UNKNOWN_OSTC = 0, + HW_Frog = HW_BLUETOOTH_COM, + HW_OSTCSport_a = HW_LIGHT_SENSOR | HW_BLUETOOTH_COM, + HW_OSTCSport_b = HW_OSTCSport_a | HW_CHARGEABLE_BATTERY, + HW_OSTC2c = HW_CHARGEABLE_BATTERY, + HW_OSTC2_a = HW_CHARGEABLE_BATTERY | HW_BLUETOOTH_COM, + HW_OSTC2_b = HW_OSTCSport_b, + HW_OSTC2_c = HW_OSTC2_b | HW_OPTICAL_COM, + HW_OSTC3 = HW_LIGHT_SENSOR | HW_OPTICAL_COM, + HW_OSTC3p_a = HW_LIGHT_SENSOR | HW_OPTICAL_COM | HW_BLUETOOTH_COM, + HW_OSTC3p_b = HW_OSTCSport_b, + HW_OSTCcR_a = HW_CHARGEABLE_BATTERY | HW_S8_COM, + HW_OSTCcR_b = HW_OSTCcR_a | HW_LIGHT_SENSOR, + HW_OSTC4 = HW_DUALCORE|HW_BLUETOOTH_COM| + HW_OPTICAL_COM|HW_LIGHT_SENSOR|HW_CHARGEABLE_BATTERY + }; + + //------------------------------------------------------------------------ + /// \brief Ask the connect device for its hardware options. + /// + /// This is used to guess the device model, even if there is no unicity. + HardwareDescriptor hardwareDescriptor(); + + //------------------------------------------------------------------------ + /// \brief Features supported by OSTC Companion on the connected device. + /// + /// Each driver (instance of this class) is requested to tell *Companion* + /// what are the supported command. + /// + enum CompanionFeatures { + PARAMETERS = (1<<0), ///< Download/Edit/Upload various parameters. + DATE = (1<<1), ///< Set date & time. + NAME = (1<<2), ///< Set custom text displayed on main screen. + ICON = (1<<3), ///< Set custom image displayed on main screen. + DUMPSCREEN = (1<<4), ///< Makes copy of current screen. + FIRMWARE = (1<<5), ///< Do firmware upgrades. + HELIUM_DIVE = (1<<6), ///< Computes deco stops for trimix dives. + CCR_DIVE = (1<<7), ///< Computes deco stops for rebreather dives. + BLUETOOTH = (1<<8), ///< Use Bluetooh communication (instead of USB) + VPM_MODEL = (1<<9), ///< Also propose VPM deco stops. + SIGNAL_CHECK = (1<<10) ///< Echo signal quality for bluetooth computer + }; + + /// \brief Tells what is supported for a given computer. + virtual CompanionFeatures supported() const = 0; + + //------------------------------------------------------------------------ + /// \brief Length of the custom text displayed by the device. + /// + /// \returns (w,h), where \a w is text width (in chars), and \a h is the + /// number of lines. + /// Used by *Companion* GUI to format user input. + virtual QSize nameSize() const = 0; + + //------------------------------------------------------------------------ + /// \brief filename matching template for compatible firmware. + /// + /// Used by the *Upgrade Firmware...* command to propose only firmwares + /// designed for the connected device. + virtual QString firmwareTemplate() const = 0; + + //------------------------------------------------------------------------ + /// \brief Read in the specific firmware file format. + /// + /// History is a bit complex here, and the published firmware have + /// different file formats (due to support tool, and/or need for + /// encryption). + /// So each driver have to implement its specific loader. + virtual void loadFirmware(HexFile&, const QString& fileName) const = 0; + + //------------------------------------------------------------------------ + /// \brief Regular expression to filter *USB* or *Bluetooth* port names. + /// + /// Used to propose only the list of ports that matches the serial ports + /// compatible with USB/Bluetooth emulation and the connected dive + /// computer. + // virtual QRegExp portTemplate() const = 0; + virtual QRegularExpression portTemplate() const = 0; + /// \} + ////////////////////////////////////////////////////////////////////////// + /// \name High level commands + /// \{ + /// + /// Commands that implement the specific protocol for a device, to perform + /// all services needed by *OSTC Copmanion*. + + //------------------------------------------------------------------------ + /// \brief Open download mode communication to the dive computer. + /// + /// Open comm port, start download mode, check the blessed reply, + /// and get the computer identity (for description() ). + /// + /// \note this mode allows common commands to be processed, but not + /// firmware upgrade. + /// + /// \returns TRUE is everything went well. + /// \sa connectServiceMode() and disconnect(). + virtual bool connect() = 0; + + //------------------------------------------------------------------------ + /// \brief Open service mode communication to the dive computer. + /// + /// Open comm port, start service mode, check the blessed reply. + /// \note this mode is mandatory to allow upgradeFW(). + /// + /// \returns TRUE is everything went well. + /// \sa connect() and disconnect(). + virtual void connectServiceMode() = 0; + + //------------------------------------------------------------------------ + /// \brief Echo a message on the connected device screen. + /// + /// Used on most devices to display commands as they are processed, + /// so the user can see *OSTC Companion* is working properly, by seeing + /// progress on the dive computer itself. + virtual void writeText(const QString& msg) = 0; + + //------------------------------------------------------------------------ + /// \brief Set HW dive computer date and time. + virtual void setDate(const QDateTime& date) = 0; + + //------------------------------------------------------------------------ + /// \brief Set HW dive computer user text. + virtual void setName(const QString& newName) = 0; + + //------------------------------------------------------------------------ + /// \brief Display signal quality at OSTC. + virtual void getSignal() = 0; + + //------------------------------------------------------------------------ + /// \brief Read all header from OSTC + virtual void getAllHeader(unsigned char* pBuffer) = 0; + + //------------------------------------------------------------------------ + /// \brief Write all header from OSTC + virtual void writeAllHeader(unsigned char* pBuffer) = 0; + + + //------------------------------------------------------------------------ + /// \brief Read all samples from OSTC + virtual void getAllSamples(unsigned char* pBuffer) = 0; + + //------------------------------------------------------------------------ + /// \brief Write all samples to OSTC + virtual void writeAllSamples(unsigned char* pBuffer) = 0; + + //------------------------------------------------------------------------ + /// \brief Set HW dive computer icon set + /// + /// *Currently only supported by Frog dive computer*. + virtual void setIcons(const QString& fileName) = 0; + + //------------------------------------------------------------------------ + /// \brief Take a snapshot of the connected computer's screen. + /// + /// *Currently only supported by OSTC mk2/2n/2c dive computers*. + virtual QImage dumpScreen() const = 0; + + //------------------------------------------------------------------------ + /// \brief Upgrade HW dive computer firmware. + /// + /// \note needs service mode connection. + /// \sa connectServiceMode(). + virtual void upgradeFW(const QString& fileName) = 0; + + //------------------------------------------------------------------------ + /// \brief Close connection. + /// + /// Exit service mode, and close everything. + /// \p closing should be set when ending Companion, so + /// an error make a won't crash if the interface is already + /// deleted. + virtual bool disconnect(bool closing = false) = 0; + + /// \} + ////////////////////////////////////////////////////////////////////////// + /// \name Low level protocol + /// \{ + /// + /// Command and methods that have to be implemented for each device + /// to retrieve device descriptions. + + //------------------------------------------------------------------------ + /// \brief List all communication ports + /// + /// That are (or might be) used by HW dive computers. + virtual QStringList listPorts() const = 0; + + //------------------------------------------------------------------------ + /// \brief Send a command, wait ack, and retry on error. + /// + /// Service common to all current H&W dive computer: send a command byte, + /// and wait it is dully acknowledged. + /// Allow up to 10x retries when the computer does not answer anything, + /// or reply something else. + /// + /// \param [in,out] serial: the connected port to use. + /// \param [in] cmd: command byte to send. + /// \param [in] retries: Optional max number of retries. Default to 10. + /// + /// \returns the ack byte (if any), or 0xFF if never received. + static unsigned char retryCommand(Serial& serial, + unsigned char cmd, + int retries = 10); + + //------------------------------------------------------------------------ + /// \brief Read and check connected dive computer identity. + /// + /// Read fw's version, serial number and custom text from connected computer. + /// + /// \throws when the connected device does not matches the driver + /// implementation. + /// + /// \sa description(), firmware(), serialNumber() and customtext(). + virtual void getIdentity() = 0; + + //------------------------------------------------------------------------ + /// \brief The fw's version found during the last getIdentty(). + /// \sa getIDentity(). + virtual int firmware() const = 0; + + //------------------------------------------------------------------------ + /// \brief The serial number found during the last getIdentty(). + /// \sa getIDentity(). + virtual int serialNumber() const = 0; + + //------------------------------------------------------------------------ + /// \brief The user-defined string found during the last getIdentty(). + /// \sa getIDentity(). + virtual QString customText() const = 0; + + //------------------------------------------------------------------------ + /// \brief A human readable description of connected computer. + /// + /// Returns driver name, and identity data found during the last call to + /// getIdentity(). + /// + /// \sa model(), getIntentity() + virtual QString description() = 0; + + /// \} + ////////////////////////////////////////////////////////////////////////// + +protected: + //------------------------------------------------------------------------ + /// \brief List serial ports for *Bluetooth* based devices. + /// + /// Ask *OS* for the list of *Bluetooth* serial emulations whose name + /// matches the portTemplate() regular expression. + /// + QStringList listBluetoothPorts() const; + + //------------------------------------------------------------------------ + /// \brief List serial ports for USB based devices. + /// + /// Ask *OS* for the list of USB serial emulation currently connected whose name + /// matches the portTemplate() regular expression. + QStringList listUSBPorts() const; + +}; + +#endif // COMPUTEROPERATIONS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HexFile.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,358 @@ +///////////////////////////////////////////////////////////////////////////// +/// \file HexFile.cpp +/// \brief Read .hex file in "Intel HEX" format. +/// \author JD Gascuel. +/// +/// \copyright (c) 2012-2016, JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "HexFile.h" + +#include "Utils/Log.h" +#include "Utils/ProgressEvent.h" + +#include <QCoreApplication> +#include <QTextStream> +#include <QtDebug> + +////////////////////////////////////////////////////////////////////////////// +// Crypto++ objects needed: + +typedef unsigned char byte; + +#include "AES/rijndael.h" + +/////////////////////////////////////////////////////////////////////////////// + +HexFile::HexFile() +: _memSize(0), +#ifdef FROG_MASTER + _baseAddress(0), +#endif + _buffer(0) +{} + +HexFile::~HexFile() +{ + delete[] _buffer; _buffer = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool HexFile::allocate(size_t memSize, unsigned char fill) +{ + delete[] _buffer; + _buffer = new unsigned char[_memSize = memSize]; + Q_CHECK_PTR(_buffer); + + memset(_buffer, fill, memSize); + return true; +} + +bool HexFile::sqwiz(size_t newMemSize) +{ + if( newMemSize >= _memSize ) { + LOG_THROW( "Squiz failed" ); + } + _memSize = newMemSize; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static size_t bytes = 0; + +void HexFile::readLine() +{ + QByteArray line = _file.readLine(); + if( line[0] != ':' ) + LOG_THROW( "Bad HEX format" ); + + unsigned char checksum = 0; + bool ok = true; + for(int i=1; ok && i<line.length(); i+=2) { + if( line[i]=='\r' || line[i]=='\n' ) + break; + checksum += line.mid(i,2).toInt(&ok,16); + } + if( ! ok ) + LOG_THROW( "Bad HEX header" ); + if( checksum != 0 ) + LOG_THROW( "Bad HEX checksum" ); + + int len = line.mid(1,2).toInt(0, 16); + int addr = line.mid(3,2).toInt(0, 16) << 8 + | line.mid(5,2).toInt(0, 16); + int type = line.mid(7,2).toInt(0, 16); + + switch( type ) + { + case 00: //---- Data record ---------------------------------------------- + if( _baseAddress == 0x300000 ) // Skip configuration bits. + return; + if( _baseAddress == 0xF00000 ) // Skip internal prom reset. + return; + + for(int i=0; i<len; i++, ++addr) + { + size_t a = _baseAddress + addr; + if( a >= _memSize ) + LOG_THROW( "BAD HEX address" ); + + if( _buffer[a] != 0xFF ) + LOG_THROW( "Double write" ); + + _buffer[a] = line.mid(9+i*2,2).toInt(&ok,16); + if( !ok ) + LOG_THROW( "Bad HEX byte" ); + bytes++; + } + break; + + case 01: //---- END OF FILE record --------------------------------------- + _file.seek(-1); // Force to end of file. + break; + + case 02: //---- Segment address record ----------------------------------- + if( len != 2 || addr != 0 ) + LOG_THROW( "Bad HEX Segment Address record" ); + _baseAddress = line.mid(9,4).toInt(0,16) << 4; + break; + + case 04: //---- Extended Linear Address Record --------------------------- + if( len != 2 || addr != 0 ) + LOG_THROW( "Bad HEX Extended Linear Address Record" ); + _baseAddress = line.mid( 9,2).toInt(0,16) << 20 + | line.mid(11,2).toInt(0,16) << 16; + break; + + default: + LOG_THROW("Bad HEX subtype"); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void HexFile::load(const QString& fileName) +{ + Q_ASSERT(_buffer); + + _file.setFileName(fileName); + if( ! _file.open(QIODevice::ReadOnly) ) + LOG_THROW("Can't open HEX file"); + + bytes = 0; + while( ! _file.atEnd() ) + { + PROGRESS(_file.pos(), _file.size()); + readLine(); + } + PROGRESS_RESET(); + + _file.close(); + + LOG_TRACE( int(bytes/1024.0f) << "KB loaded (" + << int(bytes * 100.0f / _memSize) << "% of firmware area)."); +} + +/////////////////////////////////////////////////////////////////////////////// + +const unsigned char* HexFile::data() const +{ + Q_ASSERT(_buffer); + return _buffer; +} + +/////////////////////////////////////////////////////////////////////////////// + +unsigned int HexFile::checksum() const +{ + Q_ASSERT(_buffer); + + unsigned short low = 0; + unsigned short high = 0; + for(size_t i=0; i < _memSize; ++i) + { + low += _buffer[i]; + high += low; + } + return (((unsigned int)high) << 16) + low; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef FROG_MASTER +void HexFile::saveEncrypted(const QString&, byte [16]) +{ + LOG_THROW( "No encryption" ); +} +#else +void HexFile::saveEncrypted(const QString& fileName, byte secretKey[16]) +{ + _file.setFileName(fileName); + if( ! _file.open(QIODevice::WriteOnly|QIODevice::Truncate) ) + LOG_THROW( "Can't save to encrypted file" ); + + QTextStream out(&_file); + out.setIntegerBase(16); + out.setPadChar('0'); + + //---- Generates 128 bits of random initialization vector ---------------- + Rijndael::CFB<128>::IV iv = {0}; + Rijndael::ECB<128> PRNG; PRNG.setupEncrypt(secretKey); + for(int i=0; i<sizeof iv; ++i) + iv[i] = PRNG.get_random() % 256; + + size_t bytes = 0; // encrypted fake address + for(int i0 = 0; i0 < sizeof iv; i0+=0x10) + { + out << qSetFieldWidth(1) << ":" + << qSetFieldWidth(6) << bytes + << qSetFieldWidth(2); + for(int i=0; i<0x10; i++) + out << iv[i0+i]; + out << qSetFieldWidth(1) << "\n"; + bytes += 0x10; + } + + //---- Create stream encryptor ------------------------------------------- + Rijndael::CFB<128> enc(secretKey, iv); + + //---- Process data ------------------------------------------------------ + PROGRESS(0, _memSize); + + byte encrypted[32]; + for(size_t addr = 0; addr < _memSize; addr += 0x10) + { + PROGRESS(addr, _memSize); + + enc.encrypt(_buffer + addr, encrypted); + out << qSetFieldWidth(1) << ":" + << qSetFieldWidth(6) << bytes + << qSetFieldWidth(2); + for(int i=0; i<16; i++) + out << encrypted[i]; + out << qSetFieldWidth(1) << "\n"; + bytes += 16; + } + + //---- Process data ------------------------------------------------------ + unsigned int sum = checksum(); // 33.29.BD.1D + out << qSetFieldWidth(1) << ":" + << qSetFieldWidth(6) << bytes + << qSetFieldWidth(2) + << ((sum ) & 0xff) + << ((sum>> 8) & 0xff) + << ((sum>>16) & 0xff) + << ((sum>>24) & 0xff) + << qSetFieldWidth(1) << "\n" + ; + + qDebug().nospace() << int( bytes/1024.0f) << "KB saved into " + << fileName.section('/', -1); + + PROGRESS_RESET(); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +void HexFile::loadEncrypted(const QString& fileName, unsigned char secretKey[16]) +{ + Q_ASSERT(_buffer); + + _file.setFileName(fileName); + if( ! _file.open(QIODevice::ReadOnly) ) + LOG_THROW( "Cannot open HEX file " << fileName ); + + //---- Read 128 bits of initialization vector ---------------------------- + Rijndael::CFB<128>::IV iv = {0}; + + unsigned int bytes = 0; + bool ok = true; + for(size_t i0 = 0; i0 < sizeof iv; i0+=0x10) + { + QByteArray line = _file.readLine(); + if( line[0] != ':' ) + LOG_THROW( "Bad HEX line" ); + + unsigned int readAddr = line.mid(1,6).toInt(&ok, 16); + if( !ok || readAddr != bytes ) + LOG_THROW( "Bad HEX address" ); + + for(int i=0; i<0x10; i++) + iv[i0+i] = line.mid(2*i+7,2).toInt(&ok, 16); + if( !ok ) + LOG_THROW( "Bad HEX file format" ); + + bytes += 0x10; + } + + //---- Create stream decryptor ------------------------------------------- + Rijndael::CFB<128> dec(secretKey, iv); + + //---- Process data ------------------------------------------------------ + PROGRESS(0, (int)_memSize); + + Rijndael::Block encrypted; + for(size_t addr = 0; addr < _memSize; addr += 0x10) + { + PROGRESS((int)addr, (int)_memSize); + + QByteArray line = _file.readLine(); + if( line[0] != ':' ) + LOG_THROW( "Bad HEX line" ); + + unsigned int readAddr = line.mid(1,6).toInt(&ok, 16); + if( !ok || readAddr != bytes ) + LOG_THROW( "Bad HEX address" ); + + for(int i=0; i<0x10; i++) + encrypted[i] = line.mid(2*i+7,2).toInt(&ok, 16); + if( !ok ) + LOG_THROW( "Bad HEX file format" ); + bytes += 0x10; + + dec.decrypt(encrypted, *(Rijndael::Block*)(_buffer+addr)); + } + QByteArray line = _file.readLine(); + if( bytes != (unsigned int)line.mid(1,6).toInt(&ok, 16) || !ok ) + LOG_THROW( "Bad HEX address" ); + + unsigned int sum = line.mid( 7,2).toInt(&ok, 16) + + (line.mid( 9,2).toInt(&ok, 16) << 8 ) + + (line.mid(11,2).toInt(&ok, 16) << 16) + + (line.mid(13,2).toInt(&ok, 16) << 24); + if( sum != checksum() ) + LOG_THROW( "Bad HEX checksum" ); + + PROGRESS_RESET(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HexFile.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,92 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file HexFile.h +/// \brief Read .hex file in "Intel HEX" format. +/// \author JD Gascuel. +/// +/// \copyright (c) 2012-2016, JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2012-02-12 : [jDG] Creation for Frog Companion firmware upload. +// 2012-09-22 : [jDG] Updated for OSTC Companion (OSTC3). +// 2014-07-07 : [jDG] Cleanups for Subsurface google-summer-of-code. +// 2014-07-25 : [jDG] BSD 2-clause license. + +#include <QtGlobal> +#include <QFile> + +class QProgressBar; + +///////////////////////////////////////////////////////////////////////////// +/// \brief Read .hex file in "Intel HEX" format. +/// +/// \note that OSTC3 firmware is NOT distributed in Intel HEX format, +/// but is first flatened, then encrypted, then written back +/// in a similar looking format. +class HexFile +{ + QFile _file; + size_t _memSize; + size_t _baseAddress; ///< Base for extended address modes. + + unsigned char* _buffer; + + /// \brief Read and interpret on line of the HEX file. + void readLine(); + +public: + /// \brief Create an empty object. + HexFile(); + ~HexFile(); + + /// \brief Allocate initialized space. + bool allocate(size_t memSize, unsigned char fill = 0xFF); + bool sqwiz(size_t newMemSize); + + /// \brief Load the .hex file into the internal buffer. + /// \return TRUE is ok, FAlSE if some error occurs. + void load(const QString& fileName); + + /// \brief Return a pointer to the loaded data. + const unsigned char* data() const; + + /// \brief Returns the 4 bytes checksum of the loaded data. + /// + /// This is a simple Adler32 checksum: The lower 16bits are the sum of + /// all the bytes (modulo 65536). And the second 16bits are the sum of + /// the running sum itself (also modulo 65536). + unsigned int checksum() const; + + /// \brief Save to encrypted HEX file. + void saveEncrypted(const QString& fileName, unsigned char secretKey[16]); + + /// \brief Reload encrypted HEX file. + void loadEncrypted(const QString& fileName, unsigned char secretKey[16]); +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Images/Settings.svg Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="170.079px" height="170.078px" viewBox="0 0 170.079 170.078" style="enable-background:new 0 0 170.079 170.078;" + xml:space="preserve"> +<path style="fill:#6D6E71;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;" d="M128.24,75.113l25.45-16.467 + c-0.388-0.995-0.794-1.981-1.223-2.956l-29.639,6.353c-0.559-0.901-1.151-1.779-1.771-2.637l17.203-24.94 + c-0.739-0.772-1.495-1.527-2.268-2.267l-24.94,17.203c-0.857-0.62-1.735-1.213-2.637-1.771l6.352-29.639 + c-0.975-0.429-1.96-0.835-2.956-1.223L95.346,42.22c-1.026-0.242-2.064-0.454-3.117-0.623L86.76,11.808 + c-0.536-0.012-1.067-0.041-1.605-0.041c-0.538,0-1.067,0.029-1.602,0.041l-5.469,29.789c-1.053,0.169-2.091,0.381-3.118,0.623 + L58.5,16.77c-0.996,0.388-1.981,0.794-2.956,1.223l6.353,29.639c-0.902,0.558-1.78,1.151-2.638,1.771l-24.94-17.203 + c-0.772,0.739-1.528,1.495-2.267,2.268l17.203,24.939c-0.62,0.858-1.213,1.736-1.771,2.639L17.846,55.69 + c-0.429,0.975-0.835,1.961-1.223,2.957l25.45,16.466c-0.242,1.027-0.454,2.064-0.623,3.117l-29.79,5.469 + c-0.011,0.535-0.041,1.065-0.041,1.604s0.029,1.068,0.041,1.604l29.791,5.469c0.169,1.054,0.381,2.092,0.623,3.119l-25.451,16.465 + c0.388,0.996,0.794,1.982,1.223,2.957l29.639-6.354c0.558,0.902,1.151,1.78,1.771,2.638l-17.205,24.94 + c0.74,0.772,1.495,1.528,2.267,2.268l24.941-17.204c0.857,0.619,1.735,1.212,2.638,1.771l-6.353,29.64 + c0.974,0.43,1.96,0.835,2.956,1.223l16.467-25.451c1.026,0.241,2.064,0.453,3.117,0.622l5.469,29.791 + c0.534,0.012,1.064,0.041,1.602,0.041c0.538,0,1.069-0.029,1.605-0.041l5.469-29.791c1.053-0.169,2.091-0.381,3.117-0.623 + l16.467,25.452c0.996-0.388,1.981-0.794,2.956-1.223l-6.353-29.641c0.901-0.558,1.779-1.15,2.637-1.771l24.941,17.205 + c0.771-0.74,1.527-1.495,2.267-2.268l-17.204-24.941c0.62-0.857,1.213-1.735,1.771-2.637l29.64,6.353 + c0.43-0.975,0.835-1.961,1.223-2.957l-25.451-16.465c0.242-1.027,0.454-2.065,0.623-3.118l29.79-5.468 + c0.012-0.536,0.041-1.066,0.041-1.604s-0.029-1.068-0.04-1.604l-29.791-5.469C128.693,77.178,128.481,76.14,128.24,75.113z"/> +<circle style="fill:#A7A9AC;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;" cx="85.156" cy="85.304" r="31.307"/> +<circle style="stroke:#000000;stroke-linecap:round;stroke-linejoin:round;" cx="85.156" cy="85.304" r="17.95"/> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Images/app_OSTC_Companion.svg Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="636.965px" height="500.445px" viewBox="0 0 636.965 500.445" enable-background="new 0 0 636.965 500.445" + xml:space="preserve"> +<g id="Layer_3"> + <path fill="#EF3B39" d="M142.953,210.713c-20.592-26.411-29.991-70.277-104.71-96.446c-21.283-6-8-13.333,4-8.875 + c70.11,32.084,114.913,22.379,66-34.792c-6.853-8.011,5.662-13.975,9.333-8c43.818,71.302,94.428,59.961,75.249-19.667 + c-2.391-9.922,12.209-10.83,12.479-2.333c2.438,76.919,52.221,65.316,69.272,6.632c2.909-7.196,20.862-16.575,11.356,3.901 + c-27.426,53.644-5.663,100.083-7.082,114.013L142.953,210.713z"/> + <circle fill="#EF3B39" cx="16.96" cy="98.458" r="5.266"/> + <circle fill="#EF3B39" cx="104.8" cy="52.497" r="5.265"/> + <circle fill="#EF3B39" cx="197.174" cy="25.392" r="5.265"/> + <circle fill="#EF3B39" cx="290.173" cy="26.907" r="5.265"/> +</g> +<g id="Calque_1"> + <g> + <path d="M547.047,303.156c25.313,0,66.168,74.165,76.834,69.829c4.842-1.966-2.816-25.503-25.166-103.164 + c-8.227-28.581-4.236-79.333-6.668-100.667c-2.953-25.932-12.607-4.102-13.33,25.5c-0.834,34.167-9.322,71.628-22.666,68.335 + c-26.119-6.443-92.004-97.335-255.004-106.002c-171.436-9.114-200.432,103.387-183.963,144.669 + c12.964,32.5,22.964,42.329,43.631,57c-1.5-23.835,1.333-20.671,26.333-3c2.833-16.835-22.333-37.171,21-5.838 + c1.375,1.088,1.247-2.93,1.25-4.162c0.049-20.517-3.969-21.938,11.75-7c2.394,2.271,3.844,0.774,4-1.5 + c0.655-9.49,0.5-17.335,13.168,0.162c2.166-11.662,1.57-11.532,10.832-5.662c3.386,2.146,12.408-6.895,22.834-24.171 + c-15.797,56.413-21.918,71.585-27.25,43.547c-0.281-1.479-2.012-2.23-2.07-0.349c-0.24,7.678-1.377,20.271-3.014,20.557 + c-4.332,0.75-5.578-12.536-13.078-17.454c-4.668,8.335-1.379,19.028-5.504,19.619c-4.084,0.585-3.918-7.335-13.833-13.085 + c-4.333,15.166,6.25,30.166-18.083,11.498c-9.61-7.368-5.001,7.451-4,9c2.88,4.452,7.537,19.903,74,15.336 + C353.05,389.689,527.553,303.156,547.047,303.156z"/> + <path fill="#FFFFFF" d="M194.715,254.155c13.552-8.521,29.008,1.334,32.333,10.666c5.6,15.722-4,41.999-22.667,39.334 + C175.246,299.992,183.048,261.486,194.715,254.155z"/> + <path d="M195.716,271.09c3.8-2.39,8.134,0.375,9.067,2.99c1.57,4.405-0.358,12.426-6.356,11.027 + C190.387,283.24,192.444,273.144,195.716,271.09z"/> + </g> +</g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Images/inst_OSTC_Companion.svg Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="636.965px" height="500.445px" viewBox="0 0 636.965 500.445" enable-background="new 0 0 636.965 500.445" + xml:space="preserve"> +<g id="Boite"> + <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="425.7139" y1="179.2048" x2="425.7139" y2="418.5559"> + <stop offset="0" style="stop-color:#E8C330"/> + <stop offset="1" style="stop-color:#BA7C00"/> + </linearGradient> + <polygon fill="url(#SVGID_1_)" stroke="#231F20" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" points=" + 517.705,418.555 333.723,338.772 333.723,179.205 517.705,258.988 "/> + <linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="241.731" y1="224.6086" x2="241.731" y2="374.9467"> + <stop offset="0" style="stop-color:#E8C330"/> + <stop offset="1" style="stop-color:#BA7C00"/> + </linearGradient> + <polygon fill="url(#SVGID_2_)" stroke="#231F20" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" points=" + 149.74,418.555 333.723,338.772 333.723,179.205 149.74,258.988 "/> + <linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="425.7139" y1="258.988" x2="425.7139" y2="498.3396"> + <stop offset="0" style="stop-color:#E8C330"/> + <stop offset="1" style="stop-color:#BA7C00"/> + </linearGradient> + <polygon fill="url(#SVGID_3_)" stroke="#231F20" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" points=" + 333.723,498.34 517.705,418.555 517.705,258.988 333.723,338.772 "/> + <linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="241.731" y1="258.988" x2="241.7309" y2="498.3396"> + <stop offset="0" style="stop-color:#E8C330"/> + <stop offset="1" style="stop-color:#BA7C00"/> + </linearGradient> + <polygon fill="url(#SVGID_4_)" stroke="#231F20" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" points=" + 333.723,498.34 149.74,418.555 149.74,258.988 333.723,338.772 "/> + <linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="253.0269" y1="314.3044" x2="169.0851" y2="364.6226"> + <stop offset="0" style="stop-color:#E8C330"/> + <stop offset="1" style="stop-color:#BA7C00"/> + </linearGradient> + <polygon fill="url(#SVGID_5_)" stroke="#231F20" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" points=" + 285.394,412.135 101.41,332.35 149.74,258.988 333.723,338.772 "/> + <linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="370.5898" y1="321.696" x2="538.437" y2="358.1064"> + <stop offset="0" style="stop-color:#E8C330"/> + <stop offset="1" style="stop-color:#BA7C00"/> + </linearGradient> + <polygon fill="url(#SVGID_6_)" stroke="#231F20" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" points=" + 564.895,338.772 380.913,418.555 333.723,338.772 517.705,258.988 "/> +</g> +<g> + <g id="Layer_3"> + <path fill="#EF3B39" d="M179.968,143.068c-14.767-18.94-21.507-50.397-75.088-69.164c-15.263-4.302-5.737-9.561,2.868-6.364 + c50.277,23.007,82.406,16.048,47.33-24.95c-4.915-5.745,4.06-10.021,6.693-5.737c31.423,51.132,67.716,43,53.962-14.103 + c-1.715-7.115,8.755-7.767,8.949-1.673c1.748,55.16,37.449,46.839,49.676,4.756c2.086-5.161,14.961-11.887,8.143,2.797 + c-19.667,38.469-4.06,71.771-5.078,81.76L179.968,143.068z"/> + <circle fill="#EF3B39" cx="89.617" cy="62.568" r="3.776"/> + <circle fill="#EF3B39" cx="152.608" cy="29.609" r="3.776"/> + <circle fill="#EF3B39" cx="218.851" cy="10.171" r="3.775"/> + <circle fill="#EF3B39" cx="285.542" cy="11.258" r="3.776"/> + </g> + <g id="Calque_1"> + <g> + <path d="M469.749,209.36c18.152,0,47.451,53.185,55.1,50.076c3.472-1.41-2.02-18.288-18.047-73.98 + c-5.899-20.496-3.038-56.891-4.782-72.189c-2.117-18.597-9.041-2.941-9.56,18.286c-0.598,24.502-6.684,51.366-16.254,49.004 + c-18.73-4.62-65.978-69.8-182.866-76.016C170.4,98.004,149.607,178.68,161.417,208.284c9.296,23.306,16.468,30.355,31.288,40.876 + c-1.076-17.092,0.957-14.824,18.884-2.152c2.031-12.073-16.016-26.656,15.059-4.186c0.986,0.78,0.894-2.101,0.896-2.985 + c0.035-14.711-2.847-15.732,8.426-5.019c1.717,1.628,2.756,0.555,2.869-1.076c0.47-6.806,0.358-12.432,9.442,0.117 + c1.554-8.364,1.126-8.271,7.768-4.061c2.428,1.539,8.898-4.944,16.375-17.333c-11.328,40.456-15.717,51.334-19.542,31.229 + c-0.202-1.061-1.443-1.6-1.484-0.25c-0.172,5.506-0.987,14.538-2.161,14.742c-3.106,0.537-4-8.991-9.378-12.518 + c-3.348,5.978-0.989,13.646-3.947,14.069c-2.929,0.42-2.81-5.259-9.92-9.383c-3.107,10.875,4.482,21.633-12.968,8.246 + c-6.892-5.284-3.586,5.344-2.868,6.454c2.065,3.193,5.404,14.273,53.066,10.998C330.632,271.415,455.771,209.36,469.749,209.36z" + /> + <path fill="#FFFFFF" d="M217.087,174.221c9.718-6.11,20.802,0.956,23.187,7.648c4.016,11.274-2.868,30.119-16.255,28.207 + C203.125,207.091,208.721,179.478,217.087,174.221z"/> + <path d="M217.805,186.365c2.725-1.713,5.833,0.269,6.502,2.145c1.126,3.16-0.257,8.911-4.558,7.907 + C213.983,195.077,215.459,187.839,217.805,186.365z"/> + </g> + </g> +</g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LogEditor.ui Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,251 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>editLogWnd</class> + <widget class="QDialog" name="editLogWnd"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>853</width> + <height>385</height> + </rect> + </property> + <property name="windowTitle"> + <string>OSTC Companion settings</string> + </property> + <widget class="QLabel" name="noPortLabel"> + <property name="geometry"> + <rect> + <x>9</x> + <y>34</y> + <width>16</width> + <height>16</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Ignored" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="textFormat"> + <enum>Qt::TextFormat::RichText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignmentFlag::AlignCenter</set> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton" name="WriteAllHeader"> + <property name="geometry"> + <rect> + <x>30</x> + <y>80</y> + <width>101</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>WriteAllHeader</string> + </property> + </widget> + <widget class="QPushButton" name="okB"> + <property name="geometry"> + <rect> + <x>220</x> + <y>330</y> + <width>75</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>OK</string> + </property> + <property name="shortcut"> + <string>Return</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton" name="ReadAllSamples"> + <property name="geometry"> + <rect> + <x>30</x> + <y>230</y> + <width>101</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>ReadAllSamples</string> + </property> + </widget> + <widget class="QPushButton" name="WriteAllSamples"> + <property name="geometry"> + <rect> + <x>30</x> + <y>260</y> + <width>101</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>WriteAllSamples</string> + </property> + </widget> + <widget class="QProgressBar" name="HeaderUsage"> + <property name="geometry"> + <rect> + <x>30</x> + <y>20</y> + <width>131</width> + <height>23</height> + </rect> + </property> + <property name="value"> + <number>24</number> + </property> + </widget> + <widget class="QProgressBar" name="SampleUsage"> + <property name="geometry"> + <rect> + <x>30</x> + <y>190</y> + <width>131</width> + <height>23</height> + </rect> + </property> + <property name="value"> + <number>24</number> + </property> + </widget> + <widget class="QPushButton" name="pushButton"> + <property name="geometry"> + <rect> + <x>30</x> + <y>50</y> + <width>101</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>ReadAllHeader</string> + </property> + </widget> + <widget class="QTableWidget" name="SectorView"> + <property name="geometry"> + <rect> + <x>180</x> + <y>10</y> + <width>151</width> + <height>131</height> + </rect> + </property> + </widget> + <widget class="QPushButton" name="pushButton_2"> + <property name="geometry"> + <rect> + <x>110</x> + <y>330</y> + <width>75</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>Save Dump</string> + </property> + </widget> + <widget class="QPushButton" name="LoadDump"> + <property name="geometry"> + <rect> + <x>20</x> + <y>330</y> + <width>75</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>Load Dump</string> + </property> + </widget> + <widget class="QLabel" name="label"> + <property name="geometry"> + <rect> + <x>520</x> + <y>10</y> + <width>47</width> + <height>13</height> + </rect> + </property> + <property name="text"> + <string>Info</string> + </property> + </widget> + <widget class="QTextBrowser" name="textBrowser_2"> + <property name="geometry"> + <rect> + <x>570</x> + <y>10</y> + <width>256</width> + <height>361</height> + </rect> + </property> + <property name="acceptRichText"> + <bool>true</bool> + </property> + </widget> + <widget class="QTableWidget" name="SampleView"> + <property name="geometry"> + <rect> + <x>180</x> + <y>180</y> + <width>171</width> + <height>41</height> + </rect> + </property> + </widget> + <widget class="QPushButton" name="ResetSampleAddr"> + <property name="geometry"> + <rect> + <x>340</x> + <y>10</y> + <width>101</width> + <height>23</height> + </rect> + </property> + <property name="text"> + <string>ResetSampleAddr</string> + </property> + </widget> + </widget> + <resources/> + <connections> + <connection> + <sender>okB</sender> + <signal>clicked()</signal> + <receiver>editLogWnd</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>250</x> + <y>140</y> + </hint> + <hint type="destinationlabel"> + <x>373</x> + <y>100</y> + </hint> + </hints> + </connection> + </connections> + <slots> + <slot>languageSlot(int)</slot> + <slot>resetSettingsSlot()</slot> + <slot>updatePortsSlot()</slot> + </slots> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MainWindow.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,536 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file MainWindow.cpp +/// \brief GUI for OSTC Companion. +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2014 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "MainWindow.h" + +#include "Utils/LogAppender.h" +#include "Utils/ProgressEvent.h" + +#include "ui_MainWindow.h" +#include "SettingsDialog.h" +#include "editLogDialog.h" + +#include <QString> +#include <QDateTime> +#include <QFileDialog> +#include <QInputDialog> +#include <QMenu> +#include <QMenuBar> +#include <QMessageBox> +#include <QPlainTextEdit> +#include <QProgressBar> +#include <QSettings> +#include <QTextCursor> +#include "OSTCFrogOperations.h" +#include "OSTC2cOperations.h" +#include "OSTC2Operations.h" +#include "OSTCSportOperations.h" +#include "OSTC3Operations.h" +#include "OSTC3pOperations.h" +#include "OSTC4Operations.h" +#include "OSTC_CR_Operations.h" + +extern QSettings* settings; + +////////////////////////////////////////////////////////////////////////////// + +class EXPORT LogWindow + : public LogAppender +{ + MainWindow* _window; + + //---- The <<printing>> function ----------------------------------------- + void operator()(const Log &log) override { + QString message = log.message; + + message.replace("< ", "< ") + .replace(" >", " >"); + if( ! message.isEmpty() ) + _window->statusMessage(message); + } + + //---- Reimplementing mandatory methds ----------------------------------- + const char *type() const override { + return "File"; + } + Log::Level defaultMinLevel() const override { + return Log::LEVEL_INFO; + } + const char* defaultFormat() const override { + return "%m"; + } + +public: + LogWindow(MainWindow* window) + : LogAppender(0, NULL), + _window(window) + {} +}; + +////////////////////////////////////////////////////////////////////////////// + +MainWindow::MainWindow() + : QMainWindow(NULL), + _ui(new Ui::MainWindow), + _op(0) +{ + // Connect the Log system to this window: + new LogWindow(this); + // Connect the progress system to this window: + ProgressManager::getInstance()->setMainWindow(this); + + _ui->setupUi(this); + _ui->progressBar->show(); + + _ui->editLog->setVisible(false); + + // Auto-select last model: + QString model = settings->value("Interface/computerType").toString(); + _ui->computerType->setCurrentIndex(0); + + if( model == "ostc2c" ) + _ui->computerType->setCurrentIndex(0); + else if( model == "hwOS (Bluetooth)" ) + _ui->computerType->setCurrentIndex(1); + else if( model == "hwOS (USB)" ) + _ui->computerType->setCurrentIndex(2); + else if( model == "ostc4" ) + _ui->computerType->setCurrentIndex(3); + + changeTypeSlot(); + +#ifdef Q_OS_MAC + { + QMenuBar *menuBar = new QMenuBar(this); + QMenu* help = menuBar->addMenu(tr("&Help")); + help->addAction(tr("Preferences..."), this, SLOT(settingsSlot())); + } +#endif + + setWindowTitle(QString("OSTC Companion v%1.%2 %3") + .arg(MAJOR_VERSION) + .arg(MINOR_VERSION) // kein sprintf nötig, arg konvertiert automatisch + .arg(BETA_VERSION + ? QString(" beta %1").arg(PATCH_VERSION) + : QString::number(PATCH_VERSION)) + ); + + _ui->companionUrlL->setText( QString("<html>%1 " + "<a href='https://ostc-planner.net/wp/companion" +#if defined(Q_OS_IOS) + "?os=ios" +#elif defined(Q_OS_OSX) + "?os=mac" +#elif defined(Q_OS_WIN) + "?os=win" +#elif defined(Q_OS_ANDROID) + "?os=android" +#elif defined(Q_OS_LINUX) + "?os=linux" +#else + "?os=other" +#endif +#if defined(Q_OS_DARWIN64) || defined(Q_OS_WIN64) || defined(Q_OS_LINUX ) + "&arch=64" +#else + "&arch=32" +#endif + "&version=%2.%3.%4" + "&model=%6" + "'>ostc-planner.net/wp/companion" + "</a>" + "</html>") + .arg( tr("Official web site") ) + .arg(QString::number(MAJOR_VERSION)) + .arg(QString::number(MINOR_VERSION)) + .arg(BETA_VERSION ? QString("beta%1").arg(PATCH_VERSION) + : QString("%1") .arg(PATCH_VERSION)) + .arg(_op->model()) + ); +} + + +////////////////////////////////////////////////////////////////////////////// + +MainWindow::~MainWindow() +{ + delete _ui; + delete _op; +} + +////////////////////////////////////////////////////////////////////////////// + +bool MainWindow::event(QEvent* e) +{ + if( ProgressEvent* p = dynamic_cast<ProgressEvent*>(e) ) + { + QProgressBar* w = _ui->progressBar; + + if( p->current > p->maximum && p->maximum > 0) + { + w->setMaximum(p->maximum); // Remove throttling mode, if any. + w->reset(); + } + else + { + if( ! w->isEnabled() ) + w->setEnabled(true); + if( w->maximum() != p->maximum ) + w->setMaximum(p->maximum); // Start throttling if max==0 + w->setValue(p->current); + } + return true; + } + + return QMainWindow::event(e); +} + +////////////////////////////////////////////////////////////////////////////// + +void MainWindow::changeTypeSlot() +{ + QString name; + + //---- Setup a new driver ------------------------------------------------ + delete _op; + _op = 0; + switch( _ui->computerType->currentIndex() ) { + case 0: name = "ostc2c"; + _op = new OSTC2cOperations; + break; + case 1: name = "hwOS (USB)"; + _op = new OSTC3Operations; + break; + case 2: name = "hwOS (Bluetooth)"; + _op = new OSTC3pOperations; + break; + case 3: name = "ostc4"; + _op = new OSTC4Operations; + break; + + default: + qWarning("Internal error: unknown computer type"); + return; + } + LOG_INFO(tr("%1 selected.").arg(_op->model())); + + settings->setValue("Interface/computerType", name); + settings->sync(); + + // backword compatibility >= translate name if necessary + if ( name =="hwOS (Bluetooth)") name = "ostc3p"; + if( name =="hwOS (USB)") name = "ostc3"; + + + _ui->computerImage->setPixmap( QPixmap(":/Images/" + name + "_160x120.png")); + + updateStatus(); +} + +////////////////////////////////////////////////////////////////////////////// + +void MainWindow::settingsSlot() +{ + Settings* s = new Settings(this, _op); + s->exec(); + delete s; +} + +////////////////////////////////////////////////////////////////////////////// + +void MainWindow::connectSlot() +{ + Q_ASSERT( _op ); + + try { + LOG_INFO("Connecting..."); + + //---- Already connected ? ---------------------------------------------- + if( ! _op->description().isEmpty() ) + _op->disconnect(); + + //---- (Re)connect ------------------------------------------------------ + if( _op->connect() ) { + + if( Settings::autoSetDateTime ) + dateSlot(); + + LOG_INFO("Connected: " + _op->description() ); + } + updateStatus(); + } + catch(const std::exception& e) { + LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") + .arg(tr("Error")) + .arg(e.what()) ); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void MainWindow::closeSlot() +{ + Q_ASSERT( _op ); + + try { + LOG_INFO("Disconnecting..."); + if( _op->disconnect() ) + LOG_INFO("Disconnected."); + updateStatus(); + } + catch(const std::exception& e) { + LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") + .arg(tr("Error")) + .arg(e.what()) ); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void MainWindow::updateStatus() +{ + bool ok = _op; + _ui->connectButton->setEnabled( ok ); + + // ON when supported but NOT connected, OFF once connected. + _ui->upgradeButton->setEnabled( ok + && (_op->supported() & 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->closeButton ->setEnabled( ok ); +} + +////////////////////////////////////////////////////////////////////////////// + +void MainWindow::dateSlot() +{ + Q_ASSERT( _op ); + + try { + QDateTime date = QDateTime::currentDateTime(); + + LOG_INFO(tr("Settings date & time...")); + _op->setDate( date ); + LOG_INFO( QString("Date set to %1") + .arg(date.toString("yyyy/MM/dd hh:mm:ss")) ); + } + catch(const std::exception& e) { + LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") + .arg(tr("Error")) + .arg(e.what()) ); + } +} + + +////////////////////////////////////////////////////////////////////////////// + +void MainWindow::nameSlot() +{ + Q_ASSERT( _op ); + try { + LOG_INFO(tr("Settings name...")); + + //---- Get old name, and reformat to multi-lines --------------------- + QString oldName = _op->description().section(", ", 2); + QString oldText; + QSize size = _op->nameSize(); + + for(int l=0; l<size.height(); ++l) { + QString line = oldName.left( size.width() ).leftJustified(size.width()); + if( line.contains("\n") ) { + line = line.section("\n", 0, 0); + oldName = oldName.mid(line.length()); + } + else { + oldName = oldName.mid(line.length()); + if( oldName[0] == '\n' ) + oldName = oldName.mid(1); + } + oldText += line + "|\n"; + } + + //---- Ask user ------------------------------------------------------ + + QInputDialog* d = new QInputDialog(this); + d->setWindowTitle("Set Computer Name..."); + d->setInputMode(QInputDialog::TextInput); + d->setOptions(QInputDialog::UsePlainTextEditForTextInput); + d->setTextValue(oldText); + + QPlainTextEdit* edit = d->findChild<QPlainTextEdit*>(); + assert(edit); + edit->setStyleSheet( + "background-color: black;" + "color: green;" + "font: 14pt 'Courier New';" + ); + + if( d->exec() != QDialog::Accepted ) + return; + + QString newText = d->textValue(); + delete d; + + //---- Reformat to single padded string ------------------------------ + QStringList lines = newText.split("\n"); + QString name; + for(int l=0; l<size.height(); ++l) { + if( l < lines.count() ) + name += lines[l].leftJustified(size.width(), ' ', true); + else + name += QString(size.width(), ' '); + } + + //---- Send result --------------------------------------------------- + _op->setName( name ); + _op->getIdentity(); + LOG_INFO( QString("Name set to '%1'") + .arg(_op->description().section(',', 2)).replace("\n", "|") ); + } + catch(const std::exception& e) { + LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") + .arg(tr("Error")) + .arg(e.what()) ); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void MainWindow::iconSlot() +{ + Q_ASSERT( _op ); + try { + LOG_INFO(tr("Settings icons...")); + + 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);;" + "Anything (*.*)"); + if( ! fileName.isEmpty() ) + _op->setIcons(fileName); + } + catch(const std::exception& e) { + LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") + .arg(tr("Error")) + .arg(e.what()) ); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void MainWindow::upgradeSlot() +{ + Q_ASSERT( _op ); + + try { + LOG_INFO(tr("Upgrading firmware...")); + + QString hexFile = QFileDialog::getOpenFileName(0, + "Hex File...", + Settings::currentPath, + _op->firmwareTemplate()); + if( hexFile.isEmpty() ) + return; + + Settings::currentPath = QFileInfo(hexFile).absoluteDir().path(); + Settings::save(); + + if( _op ) + _op->upgradeFW(hexFile); + } + catch(const std::exception& e) { + LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") + .arg(tr("Error")) + .arg(e.what()) ); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void MainWindow::statusMessage(const QString& msg) +{ + { // Move cursor to end of document. + QTextCursor c = _ui->console->textCursor(); + c.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); + _ui->console->setTextCursor(c); + } + _ui->console->appendHtml(msg); + _ui->console->ensureCursorVisible(); + + qApp->processEvents(QEventLoop::AllEvents, 50); +} + +void MainWindow::retranslate() +{ + _ui->retranslateUi(this); +} + +////////////////////////////////////////////////////////////////////////////// +void MainWindow::on_signalButton_clicked() +{ + Q_ASSERT( _op ); + + try { + LOG_INFO(tr("Request Bluetooth signal strength...")); + _op->getSignal(); + } + catch(const std::exception& e) { + LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") + .arg(tr("Error")) + .arg(e.what()) ); + } +} + + +void MainWindow::on_editLog_clicked() +{ + EditLogDialog* eL = new EditLogDialog(this, _op); + eL->exec(); + delete eL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MainWindow.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,127 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file MainWindow.h +/// \brief GUI for OSTC Companion. +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2014 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2013-03-17 : [jDG] Initial version. +// 2013-03-26 : [jDG] Added scan feature for HW factory programming. +// 2014-07-07 : [jDG] Cleanups for Subsurface google-summer-of-code. +// 2014-07-25 : [jDG] BSD 2-clause license. + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +namespace Ui { +class MainWindow; +} +class HardwareOperations; +class QProgressBar; + +////////////////////////////////////////////////////////////////////////////// +/// \brief GUI for OSTC Companion. +/// +/// Implement the *Companion* user interface. +/// +class MainWindow : public QMainWindow +{ + Q_OBJECT + Ui::MainWindow* _ui; ///< Compiled interface. + HardwareOperations* _op; ///< Driver for the connected dive computer. + + //------------------------------------------------------------------------ + /// \brief Update buttons enable/disable state. + /// According to connection status (not/normal/service), and high level + /// commands supported by a given computer model. + void updateStatus(); + + //------------------------------------------------------------------------ + /// \brief Display a message on the embedded console. + /// + void statusMessage(const QString &msg); + friend class LogWindow; + + //------------------------------------------------------------------------ + /// \brief Manage asynchronous ProgressEvent events. + /// + bool event(QEvent *e) override; + +public: + ////////////////////////////////////////////////////////////////////////// + /// \brief Constructor. + /// + /// Starts everything, and instanciate the driver found in settings. + MainWindow(); + + //------------------------------------------------------------------------ + /// \brief Destructor. + /// Release interface and driver. + ~MainWindow(); + + //------------------------------------------------------------------------ + /// \brief Handle Language changes. + /// Re-apply translations to all interface elements. + void retranslate(); + +public slots: + /// \brief User changed computer model. + void changeTypeSlot(); + + /// \brief Pop SettingsDialog preferences interface. + void settingsSlot(); + + /// \brief Try to connect to the device. + void connectSlot(); + + /// \brief Try to set Date & Time on the connected device. + void dateSlot(); + + /// \brief Try to change custom text on the connected device. + void nameSlot(); + + /// \brief Try to set custom image on the connected device. + void iconSlot(); + + /// \brief Try to upgrade firmware of the connected device. + void upgradeSlot(); + + /// \brief Close current connection to the device. + void closeSlot(); +private slots: + void on_signalButton_clicked(); + void on_editLog_clicked(); +}; + +#endif // MAINWINDOW_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MainWindow.ui Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,525 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>451</width> + <height>418</height> + </rect> + </property> + <property name="windowTitle"> + <string notr="true">OSTC Companion</string> + </property> + <property name="windowIcon"> + <iconset> + <normaloff>:/Images/app_OSTC_Companion.svg</normaloff>:/Images/app_OSTC_Companion.svg</iconset> + </property> + <widget class="QWidget" name="centralWidget"> + <layout class="QGridLayout" name="gridLayout"> + <item row="7" column="0" colspan="4"> + <widget class="QProgressBar" name="progressBar"> + <property name="alignment"> + <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set> + </property> + <property name="textVisible"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="computerType"> + <property name="toolTip"> + <string>H&W dive computer model.</string> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <item> + <property name="text"> + <string notr="true">OSTC2 mk.2/2n/2c</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">OSTC hwOS (USB)</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">OSTC hwOS (Bluetooth)</string> + </property> + </item> + <item> + <property name="text"> + <string notr="true">OSTC4</string> + </property> + </item> + </widget> + </item> + <item row="0" column="3" rowspan="6"> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="connectButton"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>23</height> + </size> + </property> + <property name="toolTip"> + <string>Open or re-open the last selected USB or Bluetooth port (See Preferences). +NOTE: make sure to connect the serial cable, or to set the bluetooth mode first...</string> + </property> + <property name="styleSheet"> + <string notr="true">QPushButton { + border: 1px solid blue; + border-radius: 6px; + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0.00 white, + stop:0.55 #55aaff, + stop:0.56 #4964ff, + stop:1.00 #55aaff) +}</string> + </property> + <property name="text"> + <string>Connect</string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QPushButton" name="dateButton"> + <property name="toolTip"> + <string>Set the H&W computer date and time. +Can be automatic at each connect, if asked to in the Preferences. +(Valid once device is connected).</string> + </property> + <property name="text"> + <string>Set Date && Time</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="nameButton"> + <property name="toolTip"> + <string>Change the name displayed on the H&W device. +(Valid once device is connected).</string> + </property> + <property name="text"> + <string>Set Name...</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="iconButton"> + <property name="toolTip"> + <string>Upload a customization icon. +Only supported on Frog yet. +(Valid once device is connected).</string> + </property> + <property name="text"> + <string>Set Icon...</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="signalButton"> + <property name="text"> + <string>Check Signal</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="upgradeButton"> + <property name="toolTip"> + <string>Ask for a firmware file, and upload it to the H&W device. +Can be done before or after device (re-)connection.</string> + </property> + <property name="text"> + <string>Upgrade Firmware...</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="closeButton"> + <property name="toolTip"> + <string>Close USB or Bluetooth connection to the device.</string> + </property> + <property name="text"> + <string>Close</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Orientation::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Policy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>105</width> + <height>8</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="quitButton"> + <property name="text"> + <string>Quit</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="6" column="0" colspan="4"> + <widget class="QLabel" name="companionUrlL"> + <property name="text"> + <string notr="true"><html><head/><body><p>See us on <a href="https://ostc-planner.net/wp/companion/"><span style=" text-decoration: underline; color:#0000ff;">ostc-planner.net/wp/companion</span></a></p></body></html></string> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Orientation::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0"> + <widget class="QToolButton" name="settingsButton"> + <property name="toolTip"> + <string>Open the Preferences menu.</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset> + <normaloff>:/Images/Settings.svg</normaloff>:/Images/Settings.svg</iconset> + </property> + </widget> + </item> + <item row="8" column="0" colspan="4"> + <widget class="QPlainTextEdit" name="console"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>46</height> + </size> + </property> + <property name="frameShadow"> + <enum>QFrame::Shadow::Sunken</enum> + </property> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOn</enum> + </property> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum> + </property> + <property name="undoRedoEnabled"> + <bool>false</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + <property name="centerOnScroll"> + <bool>false</bool> + </property> + <property name="tabStopWidth" stdset="0"> + <number>4</number> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="computerImage"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>160</width> + <height>120</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>160</width> + <height>120</height> + </size> + </property> + <property name="text"> + <string/> + </property> + <property name="pixmap"> + <pixmap>:/Images/frog_160x120.png</pixmap> + </property> + </widget> + </item> + <item row="2" column="2"> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Orientation::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Orientation::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Policy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>8</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="5" column="1"> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Orientation::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Policy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1"> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Orientation::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Policy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item row="5" column="2"> + <widget class="QPushButton" name="editLog"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Edit Log</string> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <layoutdefault spacing="6" margin="11"/> + <resources/> + <connections> + <connection> + <sender>computerType</sender> + <signal>activated(int)</signal> + <receiver>MainWindow</receiver> + <slot>changeTypeSlot()</slot> + <hints> + <hint type="sourcelabel"> + <x>113</x> + <y>29</y> + </hint> + <hint type="destinationlabel"> + <x>34</x> + <y>67</y> + </hint> + </hints> + </connection> + <connection> + <sender>quitButton</sender> + <signal>clicked()</signal> + <receiver>MainWindow</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel"> + <x>351</x> + <y>210</y> + </hint> + <hint type="destinationlabel"> + <x>320</x> + <y>211</y> + </hint> + </hints> + </connection> + <connection> + <sender>dateButton</sender> + <signal>clicked()</signal> + <receiver>MainWindow</receiver> + <slot>dateSlot()</slot> + <hints> + <hint type="sourcelabel"> + <x>406</x> + <y>57</y> + </hint> + <hint type="destinationlabel"> + <x>324</x> + <y>81</y> + </hint> + </hints> + </connection> + <connection> + <sender>nameButton</sender> + <signal>clicked()</signal> + <receiver>MainWindow</receiver> + <slot>nameSlot()</slot> + <hints> + <hint type="sourcelabel"> + <x>406</x> + <y>80</y> + </hint> + <hint type="destinationlabel"> + <x>324</x> + <y>111</y> + </hint> + </hints> + </connection> + <connection> + <sender>iconButton</sender> + <signal>clicked()</signal> + <receiver>MainWindow</receiver> + <slot>iconSlot()</slot> + <hints> + <hint type="sourcelabel"> + <x>406</x> + <y>103</y> + </hint> + <hint type="destinationlabel"> + <x>325</x> + <y>141</y> + </hint> + </hints> + </connection> + <connection> + <sender>upgradeButton</sender> + <signal>clicked()</signal> + <receiver>MainWindow</receiver> + <slot>upgradeSlot()</slot> + <hints> + <hint type="sourcelabel"> + <x>406</x> + <y>149</y> + </hint> + <hint type="destinationlabel"> + <x>323</x> + <y>171</y> + </hint> + </hints> + </connection> + <connection> + <sender>connectButton</sender> + <signal>clicked()</signal> + <receiver>MainWindow</receiver> + <slot>connectSlot()</slot> + <hints> + <hint type="sourcelabel"> + <x>405</x> + <y>33</y> + </hint> + <hint type="destinationlabel"> + <x>323</x> + <y>53</y> + </hint> + </hints> + </connection> + <connection> + <sender>settingsButton</sender> + <signal>clicked()</signal> + <receiver>MainWindow</receiver> + <slot>settingsSlot()</slot> + <hints> + <hint type="sourcelabel"> + <x>31</x> + <y>30</y> + </hint> + <hint type="destinationlabel"> + <x>191</x> + <y>63</y> + </hint> + </hints> + </connection> + <connection> + <sender>closeButton</sender> + <signal>clicked()</signal> + <receiver>MainWindow</receiver> + <slot>closeSlot()</slot> + <hints> + <hint type="sourcelabel"> + <x>305</x> + <y>186</y> + </hint> + <hint type="destinationlabel"> + <x>357</x> + <y>183</y> + </hint> + </hints> + </connection> + </connections> + <slots> + <slot>changeTypeSlot()</slot> + <slot>dateSlot()</slot> + <slot>nameSlot()</slot> + <slot>iconSlot()</slot> + <slot>upgradeSlot()</slot> + <slot>connectSlot()</slot> + <slot>settingsSlot()</slot> + <slot>scanSlot()</slot> + <slot>closeSlot()</slot> + </slots> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC2Operations.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,141 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC2Operations.cpp +/// \brief Implementing various operations for the new OSTC2 (hwOS) dive computer +/// \author JD Gascuel. +/// \sa ComputerOperations.h +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "OSTC2Operations.h" +#include "HexFile.h" +#include "Utils/Log.h" + +#define FIRMWARE_SIZE 0x17F40 + +#include <QApplication> +#include <QProgressBar> +#include <QRegularExpression> +extern QProgressBar* progress; + +////////////////////////////////////////////////////////////////////////////// + +OSTC2Operations::OSTC2Operations() +{ + emulatorName = "OSTC2p"; +} + +////////////////////////////////////////////////////////////////////////////// + +QStringList OSTC2Operations::listPorts() const +{ + return listBluetoothPorts(); +} + +////////////////////////////////////////////////////////////////////////////// +#if 0 +QRegExp OSTC2Operations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegExp("tty.OSTC[0-9]+.*", Qt::CaseInsensitive); +#elif defined(Q_OS_LINUX) + // Seems ok for debian, ubuntu, redhat, CentOS and SUSE (google dixit) + return QRegExp("ttyUSB.*", Qt::CaseSensitive); +#elif defined(Q_OS_WIN) + return QRegExp("COM.*", Qt::CaseSensitive); +#endif +} +#endif +QRegularExpression OSTC2Operations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegularExpression("tty.OSTC[0-9]+.*", + QRegularExpression::CaseInsensitiveOption); +#elif defined(Q_OS_LINUX) + // Debian, Ubuntu, RedHat, CentOS, SUSE + return QRegularExpression("ttyUSB.*"); // default: case-sensitive +#elif defined(Q_OS_WIN) + return QRegularExpression("COM.*"); // default: case-sensitive +#endif +} +////////////////////////////////////////////////////////////////////////////// + +//void OSTC2pOperations::loadFirmware(HexFile& hex, const QString& fileName) const +//{ +// hex.allocate(FIRMWARE_SIZE); +// hex.load(fileName, progress); +// progress->reset(); +//} + +////////////////////////////////////////////////////////////////////////////// + +QString OSTC2Operations::model() const +{ + return "OSTC2"; +} + +HardwareOperations::CompanionFeatures OSTC2Operations::supported() const +{ + // No ICON, no DUMPSCREEN. + return CompanionFeatures(PARAMETERS|DATE|NAME|FIRMWARE + |HELIUM_DIVE|CCR_DIVE|BLUETOOTH); +} + +////////////////////////////////////////////////////////////////////////////// + +QSize OSTC2Operations::nameSize() const +{ + return QSize(12, 5); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC2Operations::getIdentity() +{ + descriptionString.clear(); + + LOG_TRACE("Getting model..."); + HardwareDescriptor hw = hardwareDescriptor(); + if( hw != HW_OSTC2_a && hw != HW_OSTC2_b && hw != HW_OSTC2_c ) + LOG_THROW("Not an OSTC2."); + + LOG_TRACE("Getting identity..."); + getCommonIdentity(); + + LOG_TRACE("Found " << descriptionString); +} + +////////////////////////////////////////////////////////////////////////////// + +QString OSTC2Operations::firmwareTemplate() const +{ + return "*_firmware.hex"; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC2Operations.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,73 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC2Operations.h +/// \brief Implementing various operations for H&W new OSTC2 (BLE) dive computer +/// \author JD Gascuel. +/// \sa ComputerOperations.h +/// +/// \copyright (c) 2015-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2015-03-15 : [jDG] Creation, from OSTC3Operations.h + +#ifndef OSTC_2p_OPERATIONS_H +#define OSTC_2p_OPERATIONS_H + +#include "OSTC3Operations.h" + +////////////////////////////////////////////////////////////////////////////// +/// \brief new OSTC2 (Bluetooth) is just an OSTC3 in a different package. +class OSTC2Operations + : public OSTC3Operations +{ + /// \brief Returns "OSTC2" + QString model() const override; + CompanionFeatures supported() const override; + + QSize nameSize() const override; + + //------------------------------------------------------------------------ + /// \brief Read OSTC2 (hwOS based) computer firmware, serial and custom text. + /// Everything is formated for the description() string. + /// \throws if something goes wrong. + void getIdentity() override; + + QStringList listPorts() const override; + // QRegExp portTemplate() const override; + QRegularExpression portTemplate() const override; + + QString firmwareTemplate() const override; + + ////////////////////////////////////////////////////////////////////////// +public: + OSTC2Operations(); +}; + +#endif // OSTC_2p_OPERATIONS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC2cOperations.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,542 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC2cOperations.h +/// \brief Implementing various operations for H&W OSTC2, mk2, 2n, 2c dive computer +/// \author JD Gascuel. +/// \sa ComputerOperations.h +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "OSTC2cOperations.h" + +#include "HexFile.h" + +#include "SettingsDialog.h" + +#include "Utils/Exception.h" +#include "Utils/Log.h" +#include "Utils/ProgressEvent.h" + +#include <QDateTime> +#include <QRegularExpression> + +#define FIRMWARE_SIZE 0x17F40 + +// 64 bytes on Mk.2/2n/2c: +#define FIRMWARE_BLOCK_SIZE 0x40 + +////////////////////////////////////////////////////////////////////////////// + +OSTC2cOperations::OSTC2cOperations() + : HardwareOperations(), + _computerFirmware(0), + _computerSerial(0) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC2cOperations::readBank0(byte bank0[]) +{ + _serial.sleep(100); + _serial.purge(); + _serial.writeByte('g'); + _serial.readBlock(bank0, 256); +} + +void OSTC2cOperations::writeBank0(const byte bank0[]) +{ + _serial.sleep(100); + _serial.purge(); + _serial.writeByte('d'); + int reply = _serial.readByte(); + if( reply != 'd' ) + LOG_THROW("Write start"); + + for(int a=4; a<256; ++a) { + _serial.writeByte(bank0[a]); + reply = _serial.readByte(); + if( reply != bank0[a] ) + LOG_THROW("Write bank0 @ " << a); + } + + _serial.sleep(500); // Allow OSTC2c some time to reboot +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC2cOperations::readBank1(byte bank1[256]) +{ + _serial.sleep(100); + _serial.purge(); + _serial.writeByte('j'); + _serial.readBlock(bank1, 256); +} + +void OSTC2cOperations::getIdentity() +{ + byte bank0[256], bank1[256]; + memset(bank0, 0xFF, sizeof bank0); + memset(bank1, 0xFF, sizeof bank1); + + //---- Get a memory dump: + try { + readBank0(bank0); + readBank1(bank1); + } catch(ReadTimeout) { + LOG_THROW("No reply from OSTC Mk.2, 2n or 2c..."); + } + + // Sanity check: + if( bank0[65] == 0xFF || bank1[1] > 99 || bank1[2] > 99 ) + LOG_THROW("Not an OSTC Mk.2, 2n or 2c..."); + + _computerSerial = bank0[0] + bank0[1]*256; + _computerFirmware = bank1[1] * 100 + bank1[2]; + _customText = QString::fromLatin1((char*)bank0+65, 25).section('}', 0,0); + + _description = QString("%1 #%2, v%3.%4, %5") + .arg(model()) + .arg(_computerSerial) + .arg(bank1[1]).arg(bank1[2], 2, 10, QChar('0')) + .arg( _customText ); +} + +int OSTC2cOperations::firmware() const +{ + return _computerFirmware; +} + +int OSTC2cOperations::serialNumber() const +{ + return _computerSerial; +} + +QString OSTC2cOperations::customText() const +{ + return _customText; +} + +////////////////////////////////////////////////////////////////////////////// +#if 0 +QRegExp OSTC2cOperations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegExp("tty.usbserial-.*", Qt::CaseInsensitive); +#elif defined(Q_OS_LINUX) + // Seems ok for debian, ubuntu, redhat, CentOS and SUSE (google dixit) + return QRegExp("ttyUSB.*", Qt::CaseSensitive); +#elif defined(Q_OS_WIN) + return QRegExp("COM.*", Qt::CaseSensitive); +#endif +} +#endif +QRegularExpression OSTC2cOperations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegularExpression("tty.usbserial-.*", + QRegularExpression::CaseInsensitiveOption); +#elif defined(Q_OS_LINUX) + return QRegularExpression("ttyUSB.*"); // default: case-sensitive +#elif defined(Q_OS_WIN) + return QRegularExpression("COM.*"); // default: case-sensitive +#endif +} +QStringList OSTC2cOperations::listPorts() const +{ + return listUSBPorts(); +} + +////////////////////////////////////////////////////////////////////////////// + +QString OSTC2cOperations::model() const +{ + return "OSTC2c"; +} + +QString OSTC2cOperations::description() +{ + return _description; +} + +////////////////////////////////////////////////////////////////////////////// + +HardwareOperations::CompanionFeatures OSTC2cOperations::supported() const +{ + // No ICON. + return CompanionFeatures(PARAMETERS|DATE|NAME|DUMPSCREEN|FIRMWARE + |HELIUM_DIVE|CCR_DIVE); +} + +////////////////////////////////////////////////////////////////////////////// + +QString OSTC2cOperations::firmwareTemplate() const +{ + return "mk2_*.hex"; +} + +////////////////////////////////////////////////////////////////////////////// + +bool OSTC2cOperations::connect() +{ + try { + LOG_TRACE("Connecting " << Settings::port); + _serial.open(Settings::port, "OSTC2c"); + getIdentity(); + return true; + } + catch(const Exception& e){ + _serial.close(); + LOG_THROW("Cannot connect " << Settings::port << ": " << e.what()); + } + return false; +} + +////////////////////////////////////////////////////////////////////////////// + +QSize OSTC2cOperations::nameSize() const +{ + return QSize(25, 1); +} + +void OSTC2cOperations::writeText(const QString& msg) +{ + // No hardware support for that. Just skip it, as it is done when + // writting bank1... + LOG_TRACE(msg); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC2cOperations::connectServiceMode() +{ + try { + _serial.open(Settings::port, "OSTC2c"); + + _serial.writeByte(0xC1); + _serial.sleep(500); // 0.50 sec for bootloader to start. + + //---- Send C1C1 to start upgrading firmware ----------------------------- + unsigned char pic = 0; + unsigned char ok = '?'; + for(int retry=0; retry < 10; ++retry) { + _serial.writeByte(0xC1); + _serial.sleep(5); + _serial.writeByte(0xC1); + _serial.sleep(5); + + try { + pic = _serial.readByte(); + ok = _serial.readByte(); + break; + } + catch(const ReadTimeout&) { + if( retry == 9 ) + LOG_THROW("Cannot start firmware upgrade: timeout."); + + LOG_INFO("Connecting OSTC2c (" << (retry+1) << "/10)..."); + } + } + + //---- Check PIC type ---------------------------------------------------- + LOG_TRACE("Pic = " << int(pic)); + if( ! pic ) + LOG_THROW("Cannot sync firmware upgrade. Cannot detect chip"); + + if( pic != 0x57 + || ok != 'K' + ) + LOG_THROW( "Cannot sync firmware upgrade. Bad chip " << int(pic) << " " << ok); + + _serial.sleep(50); // Wait 0.050 sec here. + return; + } + catch(const Exception& e) { + _serial.close(); + LOG_THROW("Cannot connect " << Settings::port << ": " << e.what()); + } + return; +} + +////////////////////////////////////////////////////////////////////////////// + +bool OSTC2cOperations::disconnect(bool /*closing*/) +{ + if( ! _serial.isOpen() ) return false; + + LOG_TRACE("Disconnecting."); + + _serial.purge(); + _serial.close(); + + return true; +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC2cOperations::setDate(const QDateTime &date) +{ + unsigned char buffer[6]; + buffer[0] = date.time().hour(); + buffer[1] = date.time().minute(); + buffer[2] = date.time().second(); + buffer[3] = date.date().month(); + buffer[4] = date.date().day(); + buffer[5] = date.date().year() % 100; + + _serial.sleep(100); // Make sure last command is finished. + _serial.purge(); + _serial.writeByte('b'); // 0x62 + + unsigned char reply = _serial.readByte(); + if( reply != 'b' ) + LOG_THROW( "sync time" ); + + _serial.writeBlock( buffer, sizeof buffer); + _serial.sleep(100); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC2cOperations::setName(const QString &newName) +{ + QByteArray padded = (newName + QString(25, '}')) + .left(25) + .toLatin1(); + + byte bank0[256] = {0}; + readBank0(bank0); + memcpy(bank0+65, padded.constData(), 25); + writeBank0(bank0); + + // Then get the new identity: + getIdentity(); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC2cOperations::setIcons(const QString &/*fileName*/) +{ + LOG_THROW("Not supported"); +} + +////////////////////////////////////////////////////////////////////////////// + +QImage OSTC2cOperations::dumpScreen() const +{ + QImage image(320, 240, QImage::Format_RGB32); + + //---- Send dump screen command ------------------------------------- + unsigned char reply; + _serial.writeByte('l'); + reply = _serial.readByte(); + + if( reply != 'l' ) + LOG_THROW( "Dumpscreen command failed: " << (int)reply ); + + //---- Read image ---------------------------------------------------- + int percent = 0; + try { + for(int x=0; x<320; ++x) + { + int p = x/16; // 5% steps + if( p != percent ) + { + PROGRESS(p, 320/16); + percent = p; + } + int pix = 0, count = 0; + + for(int y=0; y<240; ++y, --count) + { + if( count <= 0 ) + { + count = _serial.readByte(); + + if( (count & 0x80) == 0 ) + pix = 0; + else if( (count & 0xC0) == 0xC0 ) + { + pix = 0xFFFF; + count &= 0x3F; + } + else + { + unsigned char bpix[2]; + _serial.readBlock(bpix, 2); + pix = (bpix[0] << 8) + bpix[1]; + count &= 0x3F; + } + count++; + } + // Bit extension 5bits --> 8bits: + // 12345123.45 = (12345 * 0b100001) / 4 + // 12345612.3456 = (123456 * 0xb1000001) / 16 + int r = (31 & (pix >> 11)) * 255/31; + int g = (63 & (pix >> 5)) * 255/63; + int b = (31 & pix ) * 255/31; + image.setPixel(x, y, qRgb( r, g, b)); + } + } + } + catch( ReadTimeout ) { + LOG_THROW("Missing image data..."); + } + + //---- Done ---------------------------------------------------------- + PROGRESS_RESET(); + LOG_INFO( "Screen dumped." ); + return image; +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC2cOperations::loadFirmware(HexFile& hex, const QString& fileName) const +{ + hex.allocate(FIRMWARE_SIZE); + hex.load(fileName); + + //---- Patch Firmware intialization GOTO --------------------------------- + memcpy((void*)(hex.data() + 0x17F38), // To bootloader vector + (void*)(hex.data() + 0x00000), // From fw reset code + 8); // Up to 8 bytes... + + static unsigned char byteCode[8] = { + 0xA0, 0xEF, 0xBF, 0xF0, // goto 0x1F740 (bootloader 19k) + 0x00, 0x00, // nop + 0x00, 0x00 // nop + }; + memcpy((void*)(hex.data() + 0x00000), // To OSTC reset vector + (void*)(byteCode), // From go back to bootloader. + 8); // Up to 8 bytes... +} + +////////////////////////////////////////////////////////////////////////////// + +static void +uploadBlock(Serial& serial, const HexFile& hex, size_t addr) +{ + const unsigned char count = FIRMWARE_BLOCK_SIZE; + assert( 0 < count && count < 255 ); + assert((addr+count) <= FIRMWARE_SIZE ); + unsigned char reply = 0; + + unsigned char header[4]; + header[0] = 0x1F & (addr >> 16); + header[1] = 0xFF & (addr >> 8); + header[2] = 0xFF & (addr); + header[3] = count; + + unsigned char crc = header[0] + header[1] + header[2] + header[3]; + for(int i=0; i<count; ++i) + crc += hex.data()[addr+i]; + crc = -crc; // Sum should make zero. + + try { + serial.writeBlock(header, sizeof header); + serial.writeBlock(hex.data()+addr, count); + serial.writeByte(crc); + } catch(...) { + goto WriteFailed; + } + + serial.sleep(20); // 18msec for a FLASH row write, plus VAT. + + reply = serial.readByte(); + if( reply != 'K' ) + { + serial.close(); + LOG_THROW( QString("Bad checksum at 0x%1").arg((unsigned int)addr, 0, 16, QChar('0'))); + } + return; + +WriteFailed: + serial.close(); + + LOG_THROW( QString("Write failed") ); +} + +void OSTC2cOperations::upgradeFW(const QString& fileName) +{ + //---- Load and check firmware --------------------------------------- + LOG_TRACE("Loading firmware '" << fileName << "'."); + HexFile hex; + loadFirmware(hex, fileName); + + //---- Enter uart_115k_bootloader ---------------------------------------- + connectServiceMode(); + + //---- Let's do it ------------------------------------------------------- + int percent = 0; + + for(size_t addr = FIRMWARE_BLOCK_SIZE; addr < FIRMWARE_SIZE; addr += FIRMWARE_BLOCK_SIZE) + { + int p = int((addr*200.0f) / float(FIRMWARE_SIZE) + 0.5f); + if( p > percent ) + { + PROGRESS(percent, 200); + percent = p; + } + uploadBlock(_serial, hex, addr); + } + PROGRESS(200, 200); + uploadBlock(_serial, hex, 0); + + PROGRESS_RESET(); + LOG_INFO("Upgrade FW send."); + + // More than 500ms --> receive timeout. + _serial.sleep(600); +} + +void OSTC2cOperations::getSignal() +{ + return; +} + +void OSTC2cOperations::getAllHeader(unsigned char* pBuffer) +{ + return; +} +void OSTC2cOperations::writeAllHeader(unsigned char* pBuffer) +{ + return; +} + +void OSTC2cOperations::getAllSamples(unsigned char* pBuffer) +{ + return; +} +void OSTC2cOperations::writeAllSamples(unsigned char* pBuffer) +{ + return; +} +//////////////////////////////////////////////////////////////////////////////
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC2cOperations.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,121 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC2cOperations.h +/// \brief Implementing various operations for H&W OSTC2, mk2, 2n, 2c dive computer +/// \author JD Gascuel. +/// \sa ComputerOperations.h +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2013-10-26 : [jDG] Creation. +// 2015-03-16 : [jDG] Renamed OSTC2c + +#ifndef OSTC_2c_OPERATIONS_H +#define OSTC_2c_OPERATIONS_H + +#include "HardwareOperations.h" + +////////////////////////////////////////////////////////////////////////////// + +class OSTC2cOperations + : public HardwareOperations +{ + int _computerFirmware; + int _computerSerial; + QString _customText; + QString _description; + + typedef unsigned char byte; + + ////////////////////////////////////////////////////////////////////////// + /// \{ \section Low level OSTC2 operations. + + void readBank0(byte bank0[256]); + void readBank1(byte bank1[256]); + void writeBank0(const byte bank0[256]); + + void getIdentity() override; + + /// \brief The fw version found during the last getIdentty(). + int firmware() const override; + + /// \brief The serial number found during the last getIdentty(). + int serialNumber() const override; + + /// \brief The user-defined string found during the last getIdentty(). + QString customText() const override; + + /// \} + ////////////////////////////////////////////////////////////////////////// + /// \{ \section Configuration management. + + //QRegExp portTemplate() const override; + QRegularExpression portTemplate() const override; + QStringList listPorts() const override; + + QString model() const override; + QString description() override; + CompanionFeatures supported() const override; + + QString firmwareTemplate() const override; + + /// \} + ////////////////////////////////////////////////////////////////////////// + /// \{ \section OSTC2 high-level commands. + + bool connect() override; + void connectServiceMode() override; + void writeText(const QString &msg) override; + QSize nameSize() const override; + void setDate(const QDateTime& date) override; + void setName(const QString& newName) override; + void getSignal() override; + void getAllHeader(unsigned char* pBuffer) override; + void writeAllHeader(unsigned char* pBuffer) override; + void getAllSamples(unsigned char* pBuffer) override; + void writeAllSamples(unsigned char* pBuffer) override; + + void setIcons(const QString& fileName) override; + QImage dumpScreen() const override; + + void upgradeFW(const QString& fileName) override; + void loadFirmware(HexFile& hex, const QString& fileName) const override; + + bool disconnect(bool closing = false) override; + + /// \} + ////////////////////////////////////////////////////////////////////////// + +public: + OSTC2cOperations(); +}; + +#endif // OSTC_2c_OPERATIONS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC3Operations.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,685 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC3Operations.cpp +/// \brief Implementing various operations for OSTC3 dive computer +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "OSTC3Operations.h" + +#include "Utils/Exception.h" +#include "Utils/Log.h" +#include "Utils/ProgressEvent.h" + +#include "SettingsDialog.h" +#include "HexFile.h" + +#include <QApplication> +#include <QDateTime> +#include <QDir> +#include <QRegularExpression> + +// Byte extration, compatible littleendian or bigendian. +#define LOW(x) ((unsigned char)((x) % 256)) +#define HIGH(x) ((unsigned char)((x / (1<<8)) % 256)) +#define UPPER(x) ((unsigned char)((x / (1<<16)) % 256)) +#define UP32(x) ((unsigned char)((x / (1<<24)) % 256)) + +#define FIRMWARE_AREA 0x3E0000 +#define FIRMWARE_SIZE 0x01E000 // 120KB +#define FIRMWARE_BLOCK 0x1000 // 4KB + +////////////////////////////////////////////////////////////////////////////// + +OSTC3Operations::OSTC3Operations() + : descriptionString(""), + emulatorName("OSTC3"), + _computerFirmware(0), + _computerSerial(0), + _connectMode(CLOSED_MODE) +{ + memset(computerText, 0, sizeof computerText); +} + +OSTC3Operations::~OSTC3Operations() +{ + if( _connectMode != CLOSED_MODE ) + disconnect(true); +} + +////////////////////////////////////////////////////////////////////////////// + +QStringList OSTC3Operations::listPorts() const +{ + return listUSBPorts(); +} + +////////////////////////////////////////////////////////////////////////////// + +bool OSTC3Operations::connect() +{ + LOG_TRACE( "Enter download mode..." ); + + try { + _connectMode = CLOSED_MODE; + _serial.open( Settings::port, emulatorName); + _serial.sleep(333); // Initial 1/3 sec. delay to first comm. + + for(int retry=0; retry < 10; ++retry) + { + // Allow for 0.1sec extra delay + try { + _serial.writeByte(0xBB); + } catch(const WriteTimeout& ) { + // Bluetooth not present: one can open the pseudo COM port, + // but we will have a timeout on first write byte... + if( retry < 9 ) { + LOG_INFO("Cannot connect to " << Settings::port <<" (" << (retry+1) << "/10)..."); + _serial.sleep(1000); + continue; + } + LOG_THROW("Cannot connect to " << model() <<"."); + return false; + } + + _serial.sleep(100); + + //---- Check acknowledge, w/o fatal timeouts. + unsigned char ok = 0; + unsigned char echo = 0; + try { + echo = _serial.readByte(); + + // Already in connect() mode ??? + if( echo == 'M' ) + break; + + ok = _serial.readByte(); + } + catch(const ReadTimeout&) { + LOG_INFO("Retry " << (retry+1) << "/10..."); + } + + if( echo != 0xBB || ok != 0x4D ) { // DOWNLOAD modes only. + if( retry < 9 ) + { + _serial.purge(); + _serial.sleep(400); + continue; + } + LOG_THROW("Unable to enter hwOS service mode"); + return false; + } + break; + } + getIdentity(); + + QString banner = Log::applicationName(); + writeText(banner); + LOG_TRACE("Connected."); + _connectMode = DOWNLOAD_MODE; + return true; + } + catch(const Exception& e) { + disconnect(); + LOG_THROW("Port " << Settings::port << ": " << e.what()); + } + return false; +} + +////////////////////////////////////////////////////////////////////////////// + +bool OSTC3Operations::disconnect(bool /*closing*/) +{ + if( _connectMode == CLOSED_MODE ) return false; + + descriptionString.clear(); // cleanup for interface updateStatus() + _connectMode = CLOSED_MODE; + + _serial.purge(); + _serial.writeByte(0xFF); // Exit communications, just in case... + _serial.sleep(100); + + _serial.purge(); + _serial.close(); + + return true; +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::getIdentity() +{ + descriptionString.clear(); + + LOG_TRACE("Getting model..."); + HardwareDescriptor hw = hardwareDescriptor(); + // if( hw != HW_OSTC3 ) + // LOG_THROW("Not an OSTC3."); + + LOG_TRACE("Getting identity..."); + getCommonIdentity(); + + LOG_INFO("Found " << descriptionString.trimmed()); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::getCommonIdentity() +{ + unsigned char echo = retryCommand(_serial, 'i'); // 0x69 + if( echo != 'i' ) + LOG_THROW("Bad identity reply (1)"); + + unsigned char header[4 + 60 + 1] = {0}; + _serial.readBlock(header, sizeof header); + if( header[64] != 0x4D ) // DOWNLOAD modes only. + LOG_THROW("Bad identity reply (2)"); + + _computerSerial = header[0] + header[1]*256; + _computerFirmware = header[2] * 100 + header[3]; + memcpy(computerText, header+4, sizeof computerText); + + descriptionString = QString("%1 #%2, fw %3.%4, %5") + .arg(model()) + .arg(serialNumber(), 4, 10, QChar('0')) + .arg(firmware() / 100).arg(firmware() % 100, 2, 10, QChar('0')) + .arg( QString::fromLatin1((char*)(computerText), 60) + .replace(QChar('\0'), " ") ); +} + +////////////////////////////////////////////////////////////////////////////// + +int OSTC3Operations::firmware() const +{ + return _computerFirmware; +} + +int OSTC3Operations::serialNumber() const +{ + return _computerSerial; +} + +QString OSTC3Operations::customText() const +{ + return QString::fromLatin1(computerText, sizeof computerText); +} + +////////////////////////////////////////////////////////////////////////////// + +QSize OSTC3Operations::nameSize() const +{ + return QSize(12, 5); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::writeText(const QString& msg) +{ + QByteArray buffer = msg.leftJustified(16, ' ', true).toLatin1(); + LOG_TRACE("Echoing string '" << buffer << "'"); + + // 2014-10-27 jDG: On OSTC3 v1.60, after an ERASE AREA, we do get + // a spurious L here (instead of n)... + unsigned char echo = retryCommand(_serial, 'n'); // 0x6E Echo string. + if( echo != 'n' ) + LOG_THROW("Bad message reply (1)"); + + _serial.writeBlock((const unsigned char*)buffer.data(), 16); + _serial.sleep(25); // Allow 25msec to display the message... + + unsigned char ok = _serial.readByte(); + if( ok != 0x4C && ok != 0x4D ) // DOWNLOAD or SERVICE modes. + LOG_THROW("Bad message reply (2)"); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::setDate(const QDateTime &date) +{ + LOG_TRACE("Set Date " << date.toString("MM/dd/yyyy hh:mm")); + + unsigned char buffer[6]; + buffer[0] = date.time().hour(); + buffer[1] = date.time().minute(); + buffer[2] = date.time().second(); + buffer[3] = date.date().month(); + buffer[4] = date.date().day(); + buffer[5] = date.date().year() % 100; + + unsigned char echo = retryCommand(_serial, 'b'); // 0x62 Sync date + if( echo != 'b' ) + LOG_THROW("Bad clock reply (1)"); + + _serial.writeBlock(buffer, sizeof buffer); + _serial.sleep(5); + + unsigned char ok = _serial.readByte(); + if( ok != 0x4D ) // DOWNLOAD mode only. + LOG_THROW("Bad clock reply (2)"); + + writeText( "Set " + date.toString("MM/dd hh:mm") ); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::setName(const QString &newName) +{ + LOG_TRACE("Set Name '" << newName << "'"); + + char buffer[60]; + memset(buffer, 0, sizeof buffer); + strncpy(buffer, newName.toLatin1().constData(), sizeof buffer); + + unsigned char echo = retryCommand(_serial, 'c'); // 0x63 Send custom text + if( echo != 'c' ) + LOG_THROW("Bad text reply (1)"); + + _serial.writeBlock((unsigned char*)buffer, sizeof buffer); + _serial.sleep(5); + + unsigned char ok = _serial.readByte(); + if( ok != 0x4D ) // DOWNLOAD modes only. + LOG_THROW("Bad text reply (2)"); + + getIdentity(); + // Echo the first line of customtext: + writeText(newName.left(12).trimmed()); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::setIcons(const QString &) +{ + LOG_THROW("Set icons: Not yet implemented"); +} + +QImage OSTC3Operations::dumpScreen() const +{ + LOG_THROW("Dump screen: Not yet implemented"); + return QImage(); +} + +////////////////////////////////////////////////////////////////////////////// + +static unsigned char ostc3SecretKey[16] = { + 241,233, 176, 48, + 69,111, 190, 85, + 255,231, 248, 49, + 19,108, 242,254 +}; + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::eraseRange(unsigned int addr, unsigned int size) +{ + // Convert size to number of pages, rounded up. + size = ((size + 4095) / 4096); + + // Erase just the needed pages. + unsigned char buffer[4]; + buffer[0] = UPPER(addr); + buffer[1] = HIGH(addr); + buffer[2] = LOW(addr); + buffer[3] = LOW(size); + + unsigned char reply = retryCommand(_serial, 'B'); // Command 'B' + if( reply != 0x42 ) + LOG_THROW("eraseRange (1)"); + + _serial.writeBlock(buffer, 4); + + // Wait (120/4)ms by block of 4K, plus 3% VAT to be sure. + _serial.sleep(40 + size * 31); + + unsigned char ok = _serial.readByte(); + if( ok != 0x4c ) // SERVICE MODE acknowledge. + LOG_THROW("eraseRange (2)"); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::writeBlock(unsigned int addr, + const unsigned char *data, + unsigned int size) +{ + unsigned char buffer[3]; + buffer[0] = UPPER(addr); + buffer[1] = HIGH(addr); + buffer[2] = LOW(addr); + + unsigned char reply = retryCommand(_serial, '0'); // 0x30 + if( reply != '0' ) + LOG_THROW("startWrite"); + + _serial.writeBlock(buffer, sizeof buffer); + _serial.sleep(2); + + _serial.writeBlock(data, size); + // Approximated EEPROM write time some 1sec timeout ?? + // 1KB = 240 + 1000 = 1240 : Ok. + // 4KB = 1.1sec : Ok. + _serial.sleep(1100); + + unsigned char ok = _serial.readByte(); + if( ok != 0x4c && ok != 0x4d ) // DOWNLOAD or SERVICE modes. + LOG_THROW("stopWrite"); +} + + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::readBlock(unsigned int addr, unsigned char* ptr, unsigned int size) +{ + unsigned char buffer[6]; + buffer[0] = UPPER(addr); + buffer[1] = HIGH(addr); + buffer[2] = LOW(addr); + buffer[3] = UPPER(size); + buffer[4] = HIGH(size); + buffer[5] = LOW(size); + + unsigned char reply = retryCommand(_serial, 0x20); // Command ' ' + if( reply != 0x20 ) + LOG_THROW("readBytes"); + + _serial.writeBlock(buffer, sizeof buffer); + _serial.sleep(500); // Allow some time to start emitting... + _serial.readBlock(ptr, size); + + // Note: in that case, the OK byte is send AFTER the data block. + unsigned char ok = _serial.readByte(); + if( ok != 0x4C ) // SERVICE modes only. + LOG_THROW("readBytes (2)"); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::upgradeFirmware(unsigned int checksum) +{ + unsigned char buffer[5]; + buffer[0] = LOW(checksum); + buffer[1] = HIGH(checksum); + buffer[2] = UPPER(checksum); + buffer[3] = UP32(checksum); + + // Compute magic checksum's checksum. + buffer[4] = 0x55; + buffer[4] ^= buffer[0]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); + buffer[4] ^= buffer[1]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); + buffer[4] ^= buffer[2]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); + buffer[4] ^= buffer[3]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); + + unsigned char reply = retryCommand(_serial, 0x50); // 'P' : send FW to bootloader + if( reply != 0x50 ) + LOG_THROW("Flashing start (1)"); + + _serial.writeBlock(buffer, sizeof buffer); + unsigned char ok = _serial.readByte(); + if( ok != 0x4C ) // SERVICE modes only. + LOG_THROW("Flashing start (2)"); + + // NOTE: the device never return, because it always do a reset, + // with ot without reprogramming... + _serial.sleep(500); + _serial.close(); + descriptionString.clear(); +} + +QString OSTC3Operations::firmwareTemplate() const +{ + return "*_firmware.hex"; +} + +#if 0 +QRegExp OSTC3Operations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegExp("tty.usbserial-.*", Qt::CaseInsensitive); +#elif defined(Q_OS_LINUX) + // Seems ok for debian, ubuntu, redhat, CentOS and SUSE (google dixit) + return QRegExp("ttyUSB.*", Qt::CaseSensitive); +#elif defined(Q_OS_WIN) + return QRegExp("COM.*", Qt::CaseSensitive); +#endif +} +#endif + +QRegularExpression OSTC3Operations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegularExpression("tty.usbserial-.*", + QRegularExpression::CaseInsensitiveOption); +#elif defined(Q_OS_LINUX) + // Debian, Ubuntu, RedHat, CentOS, SUSE + return QRegularExpression("ttyUSB.*"); // default: case-sensitive +#elif defined(Q_OS_WIN) + return QRegularExpression("COM.*"); // default: case-sensitive +#endif +} +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::connectServiceMode() +{ + LOG_TRACE( "Enter service mode..." ); + + // NOTE: Service mode requires a special starting sequence, different from + // the usual download mode state. + // 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... + + for(int retry=0; retry < 10; ++retry) + { + unsigned char serviceMode[] = { 0xAA, 0xAB, 0xCD, 0xEF }; + + try { + _serial.writeBlock(serviceMode, sizeof serviceMode); + } + catch(const WriteTimeout&) { + // Bluetooth not present: one can open the pseudo COM port, + // but we will have a timeout on first write byte... + if( retry < 9 ) { + LOG_INFO("Cannot connect to " << Settings::port <<" (" << (retry+1) << "/10)..."); + _serial.sleep(1000); + continue; + } + LOG_THROW("Cannot connect to " << model() << "."); + return; + } + + // Allow for 0.1sec extra delay + _serial.sleep(100); + + //---- Check acknowledge: + unsigned char echo = 0; + try { + echo = _serial.readByte(); + } + catch(...) {} + + if( echo != 0x4b ) { +serviceModeFailed: + if( retry < 9 ) + { + _serial.purge(); + _serial.sleep(400); + continue; + } + LOG_THROW("Unable to enter " << model() <<" service mode"); + } + + echo = _serial.readByte(); + if( echo != 0xAB ) + goto serviceModeFailed; + echo = _serial.readByte(); + if( echo != 0xCD ) + goto serviceModeFailed; + echo = _serial.readByte(); + if( echo != 0xEF ) + goto serviceModeFailed; + echo = _serial.readByte(); + if( echo != 0x4c ) // SERVICE modes only. + goto serviceModeFailed; + break; + } + _connectMode = SERVICE_MODE; +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3Operations::upgradeFW(const QString &fileName) +{ + //---- Load and check firmware ------------------------------------------- + LOG_INFO("Loading firmware."); + HexFile hex; + loadFirmware(hex, fileName); + + //---- Enter Service Mode ------------------------------------------------ + connectServiceMode(); + + //---- Erase old Firmware ------------------------------------------------ + PROGRESS(0, FIRMWARE_SIZE); + LOG_INFO("Erasing Firmware."); + + writeText(" Erasing FW..."); + eraseRange(FIRMWARE_AREA, FIRMWARE_SIZE); + + //---- Upload Firmware --------------------------------------------------- + LOG_INFO("Uploading firmware."); + writeText(" Uploading..."); + + for(int len = 0x00000; len < FIRMWARE_SIZE; len += FIRMWARE_BLOCK) + { + unsigned char percent = int(len * 100.0f / FIRMWARE_SIZE + 0.5f); + writeText( QString(" Uploading %1%") + .arg(percent, 2) ); + PROGRESS(percent, 100); + + writeBlock(FIRMWARE_AREA+len, hex.data()+len, FIRMWARE_BLOCK); + } + PROGRESS(100, 100); + + //---- Verify firmware --------------------------------------------------- + LOG_INFO("Verify firmware."); + writeText(" Verifying..."); + { + unsigned char* buffer = new unsigned char[FIRMWARE_SIZE]; + Q_CHECK_PTR(buffer); + + for(int len = 0x00000; len < FIRMWARE_SIZE; len += FIRMWARE_BLOCK) + { + unsigned char percent = int(len * 100.0f / FIRMWARE_SIZE + 0.5f); + writeText( QString(" Verifying %1%") + .arg(percent, 2) ); + PROGRESS(percent, 100); + + readBlock(FIRMWARE_AREA+len, buffer+len, FIRMWARE_BLOCK); + } + PROGRESS(100, 100); + qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 10); + + for(int i=0; i<FIRMWARE_SIZE; ++i) + if( buffer[i] != hex.data()[i] ) + { + writeText(" Verify FAILED"); + LOG_THROW("readback is different"); + } + + delete[] buffer; + } + PROGRESS_THROTTLE(); + + //---- Flashing firmware ------------------------------------------------- + LOG_INFO("Programming."); + writeText(" Programming..."); + upgradeFirmware( hex.checksum() ); + + //---- Done -------------------------------------------------------------- + // Low-level close, to avoid trying to send a 0xFF byte... + _serial.close(); + _connectMode = CLOSED_MODE; + LOG_INFO("Upgrade done."); + PROGRESS_RESET(); +} + +void OSTC3Operations::loadFirmware(HexFile& hex, const QString& fileName) const +{ + hex.allocate(FIRMWARE_SIZE); + hex.loadEncrypted(fileName, ostc3SecretKey); +} + +////////////////////////////////////////////////////////////////////////////// + +QString OSTC3Operations::model() const +{ + return "OSTC hwOS (USB)"; +} + +QString OSTC3Operations::description() +{ + return descriptionString; +} + +////////////////////////////////////////////////////////////////////////////// + +HardwareOperations::CompanionFeatures OSTC3Operations::supported() const +{ + // No ICON, no DUMPSCREEN, no VPM + return CompanionFeatures(PARAMETERS|DATE|NAME|FIRMWARE + |HELIUM_DIVE|CCR_DIVE); +} + +////////////////////////////////////////////////////////////////////////////// +void OSTC3Operations::getSignal() +{ + return; +} +void OSTC3Operations::getAllHeader(unsigned char* pBuffer) +{ + return; +} +void OSTC3Operations::writeAllHeader(unsigned char* pBuffer) +{ + return; +} + +void OSTC3Operations::getAllSamples(unsigned char* pBuffer) +{ + return; +} +void OSTC3Operations::writeAllSamples(unsigned char* pBuffer) +{ + return; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC3Operations.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,193 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC3Operations.h +/// \brief Implementing various operations for H&W OSTC3 dive computer +/// \author JD Gascuel. +/// \sa HardwareOperations.h +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2013-03-17 : [jDG] Creation. +// 2014-07-07 : [jDG] Cleanups for Subsurface google-summer-of-code. +// 2014-07-25 : [jDG] BSD 2-clause license. + +#ifndef OSTC3OPERATIONS_H +#define OSTC3OPERATIONS_H + +#include "HardwareOperations.h" + +////////////////////////////////////////////////////////////////////////////// +/// \brief Implementing various low-level operations for OSTC3 dive computer +/// +/// \sa OSTCSportOperations, OSTC2Operations, OSTC3pOperations, +/// OSTC4Operations, OSTCcROperations. +class OSTC3Operations + : public HardwareOperations +{ + ////////////////////////////////////////////////////////////////////////// + /// \{ \section Configuration management. + + // QRegExp portTemplate() const override; + QRegularExpression portTemplate() const override; + QStringList listPorts() const override; + QString model() const override; + QString description() override; + CompanionFeatures supported() const override; + + /// \} + ////////////////////////////////////////////////////////////////////////// +protected: + + //------------------------------------------------------------------------ + /// \brief Erase OSTC3 PROM memory. + /// PROM memory should be erased beforeprogramming the new firmware. + /// This command is used to erase a set of 4KB pages. + /// \param[in] addr: First address (24bits) to erase. Should be a multiple of 4096. + /// \param[in] size: Number of bytes to erase. Will be rounded (up) to a multiple of 4096. + /// \throws if something goes wrong. + void eraseRange(unsigned int addr, unsigned int size); + + //------------------------------------------------------------------------ + /// \brief Write a big block of bytes to OSTC3 RAM memory. + /// + /// \param[in] addr: First address (24bits) to write to. + /// \param[in] data: Bytes to write. + /// \param[in] size: Number of bytes to write. + /// \throws if something goes wrong. + void writeBlock(unsigned int addr, + const unsigned char *data, unsigned int size); + + //------------------------------------------------------------------------ + /// \brief Read-back a big block of bytes from OSTC3 RAM memory. + /// + /// \param[in] addr: First address (24bits) to read from. + /// \param[in] ptr : Where to store bytes read. + /// \param[in] size: Number of bytes to read. + /// \throws if something goes wrong. + void readBlock (unsigned int addr, unsigned char *ptr, unsigned int size); + + //------------------------------------------------------------------------ + /// \brief Burn firmare. + /// + /// Firmware should be first uploaded to RAM memory (\sa writeBlock() ), + /// in area 0x3E0000 .. 0x3FE000, + /// then the firmware command will tell the OSTC3 to use that to reprogramm + /// itself. + /// A validation checksum is done to make sure a valid data have been + /// uploaded. + /// + /// \param[in] checksum: Adler32 checksum of the new firmware. + /// \throws if something goes wrong. + void upgradeFirmware(unsigned int checksum); + +public: + OSTC3Operations(); + ~OSTC3Operations(); + + /// \brief The fw version found during the last getIdentty(). + int firmware() const override; + + /// \brief The serial number found during the last getIdentty(). + int serialNumber() const override; + + /// \brief The user-defined string found during the last getIdentty(). + QString customText() const override; + + ////////////////////////////////////////////////////////////////////////// + /// \{ \section OSTC3 low-level service mode commands. + /// + /// Low level commands are used to directly speak to the OSTC3 firmware. + /// They all throw an Exception if some error occurs. + + //------------------------------------------------------------------------ + /// \brief Custom text size (lines and columns). + QSize nameSize() const override; + + //------------------------------------------------------------------------ + /// \brief Read OSTC3 computer firmware, serial and custom text. + /// Everything is formated for the description() string. + /// \throws if something goes wrong. + void getIdentity() override; + + //------------------------------------------------------------------------ + /// \brief Display a short text on OSTC3 while on service mode. + /// OSTC3 can handle 16 chars. So the string is automatically + /// padded with spaces to clean any leftover. + /// \throws if something goes wrong. + void writeText(const QString &msg) override; + + QString firmwareTemplate() const override; + + /// \} + ////////////////////////////////////////////////////////////////////////// + /// \{ \section OSTC3 high-level commands. + + bool connect() override; + bool disconnect(bool closing = false) override; + void setDate(const QDateTime& date) override; + void setName(const QString& newName) override; + void getSignal() override; + void getAllHeader(unsigned char* pBuffer) override; + void writeAllHeader(unsigned char* pBuffer) override; + void getAllSamples(unsigned char* pBuffer) override; + void writeAllSamples(unsigned char* pBuffer) override; + void setIcons(const QString& fileName) override; + QImage dumpScreen() const override; + void upgradeFW(const QString& fileName) override; + + void loadFirmware(HexFile& hex, const QString& fileName) const override; + + /// \} + + ////////////////////////////////////////////////////////////////////////// +protected: + QString descriptionString; + QString emulatorName; + char computerText[60]; + + virtual void getCommonIdentity(); + + void connectServiceMode() override; + + ////////////////////////////////////////////////////////////////////////// +private: + unsigned short _computerFirmware; + unsigned short _computerSerial; + +protected: + enum Mode { + CLOSED_MODE = 0, ///< Not yet open. + DOWNLOAD_MODE, ///< Open in normal mode. + SERVICE_MODE ///< Open in FIRMWARE UPGRADE mode. + } _connectMode; +}; + +#endif // OSTC3OPERATIONS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC3pOperations.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,126 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC3pOperations.h +/// \brief Implementing various operations for new H&W OSTC3 (BLE) dive computer +/// \author JD Gascuel. +/// \sa OSTC3Operations.h +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "OSTC3pOperations.h" +#include "Utils/Log.h" +#include <QRegularExpression> + +////////////////////////////////////////////////////////////////////////////// + +OSTC3pOperations::OSTC3pOperations() +{ + emulatorName = "OSTC3p"; +} + +////////////////////////////////////////////////////////////////////////////// + +QStringList OSTC3pOperations::listPorts() const +{ + return listBluetoothPorts(); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC3pOperations::getIdentity() +{ + descriptionString.clear(); + + LOG_TRACE("Getting model..."); + HardwareDescriptor hw = hardwareDescriptor(); + +// if( hw != HW_OSTC3p_a && hw != HW_OSTC3p_b ) + // LOG_THROW("Not an OSTC3+."); + + LOG_TRACE("Getting identity..."); + getCommonIdentity(); + + LOG_TRACE("Found " << descriptionString); +} + +////////////////////////////////////////////////////////////////////////////// + +QString OSTC3pOperations::model() const +{ + return "OSTC hwOS (Bluetooth)"; +} + +QString OSTC3pOperations::firmwareTemplate() const +{ + return "*_firmware.hex"; +} + +////////////////////////////////////////////////////////////////////////////// + +HardwareOperations::CompanionFeatures OSTC3pOperations::supported() const +{ + // No ICON, no DUMPSCREEN, no VPM + return CompanionFeatures(PARAMETERS|DATE|NAME|FIRMWARE + |HELIUM_DIVE|CCR_DIVE|BLUETOOTH); +} +#if 0 +QRegExp OSTC3pOperations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegExp("tty.OSTC[0-9][0-9]+.*", Qt::CaseInsensitive); +#elif defined(Q_OS_LINUX) + // Seems ok for debian, ubuntu, redhat, CentOS and SUSE (google dixit) + return QRegExp("ttyUSB.*", Qt::CaseSensitive); +#elif defined(Q_OS_WIN) + return QRegExp("COM.*", Qt::CaseSensitive); +#endif +} +#endif + +#include <QRegularExpression> + +QRegularExpression OSTC3pOperations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegularExpression("tty.OSTC[0-9][0-9]+.*", + QRegularExpression::CaseInsensitiveOption); +#elif defined(Q_OS_LINUX) + // Debian, Ubuntu, RedHat, CentOS, SUSE + return QRegularExpression("ttyUSB.*"); // default: case-sensitive +#elif defined(Q_OS_WIN) + return QRegularExpression("COM.*"); // default: case-sensitive +#endif +} + +void OSTC3pOperations::getSignal() +{ + return; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC3pOperations.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,62 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC3pOperations.h +/// \brief Implementing various operations for new H&W OSTC3 (BLE) dive computer +/// \author JD Gascuel. +/// \sa OSTC3Operations.h +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2015-03-16 : [jDG] Creation. + +#ifndef OSTC_3p_OPERATIONS_H +#define OSTC_3p_OPERATIONS_H + +#include "OSTC3Operations.h" + +class OSTC3pOperations + : public OSTC3Operations +{ + // Re-implemented various stuff + QStringList listPorts() const override; + HardwareOperations::CompanionFeatures supported() const override; + QString firmwareTemplate() const override; + // QRegExp portTemplate() const override; + QRegularExpression portTemplate() const override; + QString model() const override; + void getIdentity() override; + void getSignal() override; + +public: + OSTC3pOperations(); +}; + +#endif // OSTC_3p_OPERATIONS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC4Operations.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,591 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC4Operations.h +/// \brief Implementing various operations for H&W OSTC4 dive computer +/// \author JD Gascuel. +/// \sa OSTC3Operations, HardwareOperations.h +/// +/// \copyright (c) 2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +#include <QSettings> +#include <QRegularExpression> +#include "OSTC4Operations.h" + +#include "Utils/Log.h" +#include "Utils/Exception.h" +#include "Utils/ProgressEvent.h" + +extern QSettings* settings; + +#define FIRMWARE_BLOCK 0x40 // 64 bytes +#define FIRMWARE_BLOCK_DELAY 15 // 15 msec. +#define FIRMWARE_BLOCK_FAST 0x300 // 4096 bytes + +////////////////////////////////////////////////////////////////////////////// + +QSize OSTC4Operations::nameSize() const +{ + return QSize(12, 4); +} + +////////////////////////////////////////////////////////////////////////////// + +QString OSTC4Operations::model() const +{ + return "OSTC4"; +} + +HardwareOperations::CompanionFeatures OSTC4Operations::supported() const +{ + // No ICON, no DUMPSCREEN + return CompanionFeatures(PARAMETERS|DATE|NAME|FIRMWARE + |HELIUM_DIVE|CCR_DIVE|BLUETOOTH|VPM_MODEL|SIGNAL_CHECK); +} + +QString OSTC4Operations::firmwareTemplate() const +{ + return "ostc4*.bin"; +} + +QStringList OSTC4Operations::listPorts() const +{ + return listBluetoothPorts(); +} +#if 0 +QRegExp OSTC4Operations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegExp("tty[.]OSTC4-.*", Qt::CaseInsensitive); +#elif defined(Q_OS_LINUX) + // Seems ok for debian, ubuntu, redhat, CentOS and SUSE (google dixit) + return QRegExp("ttyUSB.*", Qt::CaseSensitive); +#elif defined(Q_OS_WIN) + return QRegExp("COM.*", Qt::CaseSensitive); +#endif +} +#endif + + +QRegularExpression OSTC4Operations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegularExpression("tty[.]OSTC4-.*", + QRegularExpression::CaseInsensitiveOption); +#elif defined(Q_OS_LINUX) + // Debian, Ubuntu, RedHat, CentOS, SUSE + return QRegularExpression("ttyUSB.*"); // default: case-sensitive +#elif defined(Q_OS_WIN) + return QRegularExpression("COM.*"); // default: case-sensitive +#endif +} +////////////////////////////////////////////////////////////////////////////// + +void OSTC4Operations::getIdentity() +{ + descriptionString.clear(); + + LOG_TRACE("Getting model..."); + HardwareDescriptor hw = hardwareDescriptor(); + if( hw != HW_OSTC4 ) + LOG_THROW("Not an OSTC4."); + + LOG_TRACE("Getting identity..."); + getCommonIdentity(); + + //---- Main firmware ----------------------------------------------------- + QString mainFW; + { + unsigned char echo = retryCommand(_serial, 'k'); // 0x69 OSTC4 FW Details. + if( echo != 'k' ) + LOG_THROW("Bad firmware details."); + + unsigned char reply[4+1]; + _serial.writeByte(0xFF); // Main firmware + _serial.readBlock(reply, sizeof reply); + if( reply[4] != 0x4D ) + LOG_THROW("Bad main firmware reply."); + + mainFW = QString::asprintf("%d.%02d.%02d%s", reply[0], reply[1], reply[2], reply[3] ? "beta" : ""); + } + + //---- RTE firmware ------------------------------------------------------ + QString rteFW; + { + unsigned char echo = retryCommand(_serial, 'k'); // 0x69 OSTC4 FW Details. + if( echo != 'k' ) + LOG_THROW("Bad firmware details."); + + unsigned char reply[4+1]; + _serial.writeByte(0xFE); // RTE firmware + _serial.readBlock(reply, sizeof reply); + if( reply[4] != 0x4D ) + LOG_THROW("Bad RTE firmware reply."); + + rteFW = QString::asprintf("%d.%02d.%02d%s", reply[0], reply[1], reply[2], reply[3] ? "beta" : ""); + } + + //---- Font Package ------------------------------------------------------ + QString fontPKG; + { + unsigned char echo = retryCommand(_serial, 'k'); // 0x69 OSTC4 FW Details. + if( echo != 'k' ) + LOG_THROW("Bad firmware details."); + + unsigned char reply[4+1]; + _serial.writeByte(0x10); // Font Package + _serial.readBlock(reply, sizeof reply); + if( reply[4] != 0x4D ) + LOG_THROW("Bad Font package reply."); + + fontPKG = QString::asprintf("%d.%02d.%02d%s", reply[0], reply[1], reply[2], reply[3] ? "beta" : ""); + } + + //---- OSTC4 specific description ---------------------------------------- + descriptionString = QString("%1 #%2, fw %3 (main %4/RTE %5/Font %6), %7") + .arg(model()) + .arg(serialNumber(), 4, 10, QChar('0')) + .arg( QString::asprintf("%d.%02d", firmware() / 100, firmware() % 100)) + .arg( mainFW ) + .arg( rteFW ) + .arg( fontPKG ) + .arg( QString::fromLatin1((char*)(computerText), 60) + .replace(QChar('\0'), "") + .trimmed() ); + + LOG_INFO("Found " << descriptionString); +} + +////////////////////////////////////////////////////////////////////////////// + +OSTC4Operations::OSTC4Operations() + : fileChecksum(0,0) +{ + emulatorName = "OSTC4"; +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC4Operations::upgradeFW(const QString &fileName) +{ + LOG_INFO("Starting OSTC4 firmware upgrade..."); + + openFirmware(fileName, true); + openFirmware(fileName, false); + + LOG_INFO("Please wait for OSTC4 to complete installation..."); +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +/// 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) +{ + LOG_TRACE("Opening OSTC4 firmware '" << fileName << "'..."); + + bool forceFirmwareUpdate; + bool forceRTEUpdate; + bool forceFontlibUpdate; + bool useFastMode; + bool firmwareSupportFastMode = false; + int size; + int blockSize; + int transferDelay; + + // Previous trial failed ? + if( _file.isOpen() ) _file.close(); + + _file.setFileName(fileName); + if( ! _file.open(QIODevice::ReadOnly) ) + LOG_THROW( "Cannot open BIN file " << fileName ); + + + // Get settings to overwrite version check + forceFirmwareUpdate = settings->value("OSTC/forceFirmwareUpdate", false).toBool(); + forceRTEUpdate = settings->value("OSTC/forceRTEUpdate", false).toBool(); + forceFontlibUpdate = settings->value("OSTC/forceFontlibUpdate", false).toBool(); + + useFastMode = settings->value("OSTC/useFastMode", false).toBool(); + + //---- Check consistency ------------------------------------------------- + fileChecksum.reset(0, 0); + for(int part=1; part<4; ++part) + { + // Less than 3 parts ? + if( (_file.size() - _file.pos()) < 16 ) + break; + + if( dryRun ) + LOG_TRACE("Checking part " << part <<"..."); + + FirmwareOSTC4 header; + QByteArray bin; + loadFirmwarePart(header, bin, part, dryRun); + + QString name = QString::asprintf("%s v%d.%02d.%02d%s", + header.type==0x10 ? "Font" + : header.type==0xFF ? "FW " + : header.type==0xFE ? "RTE " + : "??? ", + header.version.x, header.version.y, header.version.z, + header.version.beta ? " beta" : ""); + + //---- On first pass, just check file structure ---------------------- + if( dryRun ) + continue; + + //---- On second pass, do upload new stuff --------------------------- + if( _connectMode != SERVICE_MODE ) + connectServiceMode(); + if( _connectMode != SERVICE_MODE ) + LOG_THROW("Cannot connect OSTC4 service mode..."); + + //---- Check if needed ? --------------------------------------------- + LOG_TRACE("Check part " << part << " is needed ?"); + unsigned char echo = retryCommand(_serial, 'k'); // 0x69 OSTC4 FW Details. + if( echo != 'k' ) + LOG_THROW("Bad firmware details."); + + unsigned char reply[4+1]; + _serial.writeByte(header.type); // type of firmware part + _serial.readBlock(reply, sizeof reply); + if( reply[4] != 0x4C ) // SERVICE MODE only. + LOG_THROW("Bad firmware reply."); + + if(((reply[0] *100 + reply[1] * 10 + reply[2]) > 151 ) && (header.type==0xFF)) /* suitable firmware version? First supported in 1.5.2 */ + { + firmwareSupportFastMode = true; + } + + if(( header.version.x == reply[0] + && header.version.y == reply[1] + && header.version.z == reply[2] ) + && (!(forceFirmwareUpdate && header.type==0xFF)) + && (!(forceRTEUpdate && header.type==0xFE)) + && (!(forceFontlibUpdate && header.type==0x10)) + ) + { + LOG_INFO(" part " << part << ": " << name << " already up-to-date."); + continue; + } + if((useFastMode) && (firmwareSupportFastMode)) /* is fast mode supported ? */ + { + blockSize = FIRMWARE_BLOCK_FAST; + transferDelay = 1; + } + else { + blockSize = FIRMWARE_BLOCK; + transferDelay = FIRMWARE_BLOCK_DELAY; + } + //---- Upload firwmare ----------------------------------------------- + const int steps = 3 + (bin.size()+blockSize-1) / blockSize; + int step = 0; + PROGRESS(step++, steps); + + LOG_INFO(" uploading part " << part + << ": " << name + << "..."); + writeText( QString("Upload Part %1").arg(part) ); + + //---- Command + PROGRESS(step++, steps); + echo = retryCommand(_serial, 's'); // 0x73 OSTC4 FW Upgrade + if( echo != 's' ) + LOG_THROW("Bad OSTC4 FW upgrade commande."); + + //---- 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); + } + + PROGRESS(steps, steps); // 100% + + //---- Wait acknowledge ---------------------------------------------- + // RTE seems to miss the acknowledge byte ... + if( header.type != 0xFE ) { + PROGRESS_THROTTLE(); + + for(int wait=0; wait<2*60; ++wait) // Up to 2 min... + { + try { + unsigned char reply = _serial.readByte(); + if( reply == 0x4C ) + break; + } + catch( const ReadTimeout&) {} + } + } + + LOG_INFO(" part " << part << ": " << name << " uploaded."); + } + + //---- Done -------------------------------------------------------------- + // Low-level close, to avoid trying to send a 0xFF byte... + if( !dryRun ) { + _serial.close(); + _connectMode = CLOSED_MODE; + } + + PROGRESS_RESET(); + + //---- Check FILE checksum on first pass --------------------------------- + if( dryRun ) { + uint expected; + if( 4 != _file.read((char*)&expected, sizeof expected) ) + LOG_THROW("Missing file checksum."); + + uint checksum = fileChecksum.a() | (fileChecksum.b() << 16); + + if( checksum != expected ) + LOG_ERROR( QString::asprintf("File checksum ERROR: expected = %04X, actual = %04X", expected, checksum) ); + else if( dryRun ) + LOG_TRACE( QString::asprintf("File checksum = %04X (OK)", checksum) ); + + if( ! _file.atEnd() ) + LOG_THROW("Extra data after last block."); + } + + _file.close(); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTC4Operations::loadFirmwarePart(FirmwareOSTC4& header, + QByteArray& bin, + int part, + bool dryRun) +{ + memset(&header, 0, sizeof header); + + //---- Read part header and check consistency ---------------------------- + if( sizeof header != _file.read((char*)&header, sizeof header) ) + LOG_THROW("EOF in header."); + fileChecksum.add(&header, sizeof header); + + uint checksum = SWAP4BYTES(header.length) + + SWAP4BYTES(*(uint*)&header.type); + if( checksum != header.checksum ) + LOG_THROW( QString::asprintf("Inconsistent header (%04X != %04X).", checksum, header.checksum) ); + + if( SWAP4BYTES(header.length) > (1024 * 1024) || (SWAP4BYTES(header.length) % 4) ) + LOG_THROW("Inconsistent header (part=" << part << ", size=" << SWAP4BYTES(header.length) << ")."); + + if( dryRun ) + switch( header.type ) { + case 0x10: LOG_TRACE("... font"); break; + case 0xFF: LOG_TRACE("... firmware"); break; + case 0xFE: LOG_TRACE("... RTE"); break; + default: + // LOG_THROW("Inconsistent header (part=" << part << ", type=0x" << QString::asprintf(int(header.type), 16) << ")."); + LOG_THROW("Inconsistent header (part=" << part << ", type=0x" << QString::asprintf("Value = %d", header.type) << ")."); + break; + } + + if( dryRun ) + LOG_TRACE("... version " << QString::asprintf("%d.%02d.%02d.%s", + header.version.x, header.version.y, header.version.z, + header.version.beta ? " beta" : "")); + + //---- Read Binary Data -------------------------------------------------- + if( dryRun ) + LOG_TRACE("... size " << SWAP4BYTES(header.length) << " bytes."); + bin.resize(SWAP4BYTES(header.length) + 4); + assert((uint)bin.size() == SWAP4BYTES(header.length) + 4); + + if( bin.size() != _file.read(bin.data(), bin.size()) ) + LOG_THROW("EOF in data."); + + if( dryRun ) + LOG_TRACE("... checksum " << QString::asprintf("%08x", + *(uint*)(bin.data() + SWAP4BYTES(header.length)))); + fileChecksum.add(bin); +} + +void OSTC4Operations::getSignal() +{ + char buffer[60]; + memset(buffer, 0, sizeof buffer); + unsigned char echo = retryCommand(_serial, 'l'); // 0x6c request bluetooth signal + if( echo != 0x6C ) + LOG_THROW("Bad text reply (1)" << echo); + + _serial.sleep(1000); + + unsigned char ok = _serial.readByte(); + if( ok != 0x4D ) // DOWNLOAD mode only. + LOG_THROW("Bad clock reply (2)"); +} + + +void OSTC4Operations::getAllHeader(unsigned char* pBuffer) +{ + unsigned char index = 0; + char buffer[60]; + memset(buffer, 0, sizeof buffer); + + if( _connectMode != SERVICE_MODE ) + connectServiceMode(); + if( _connectMode != SERVICE_MODE ) + LOG_THROW("Cannot connect OSTC4 service mode..."); + + + unsigned char echo = retryCommand(_serial, 0x85); // 0x85 request all headers (dump) + if( echo != 0x85 ) + LOG_THROW("Bad text reply (1)" << echo); + + _serial.sleep(1000); + + for(index = 0; index < 8; index++) // Expect 8 blocks a 0x8000 + { + _serial.readBlock(pBuffer + (index * 0x8000), 0x8000); + LOG_INFO((".")); + } + *(pBuffer + (index * 0x8000)) = _serial.readByte(); // get lastdiveID + LOG_INFO(QString::asprintf("%d",*(pBuffer + (index * 0x8000)))); + unsigned char ok = _serial.readByte(); + if( ok != 0x4C ) //Service mode only. + LOG_THROW("Bad clock reply (2)"); +} + +void OSTC4Operations::writeAllHeader(unsigned char* pBuffer) +{ + unsigned char index = 0; + char buffer[60]; + memset(buffer, 0, sizeof buffer); + + if( _connectMode != SERVICE_MODE ) + connectServiceMode(); + if( _connectMode != SERVICE_MODE ) + LOG_THROW("Cannot connect OSTC4 service mode..."); + + + unsigned char echo = retryCommand(_serial, 0x86); // 0x86 request all headers (dump) + if( echo != 0x86 ) + LOG_THROW("Bad text reply (1)" << echo); + + _serial.sleep(1000); + + for(index = 0; index < 8; index++) // Expect 8 blocks a 0x8000 + { + _serial.writeBlock(pBuffer + (index * 0x8000), 0x8000); + LOG_INFO((".")); + } + _serial.writeByte(*(pBuffer + (index * 0x8000))); // write lastdiveID + + unsigned char ok = _serial.readByte(); + if( ok != 0x4C ) //Service mode only. + LOG_THROW("Bad clock reply (2)"); +} + +void OSTC4Operations::getAllSamples(unsigned char* pBuffer) +{ + unsigned short index = 0; + char buffer[60]; + memset(buffer, 0, sizeof buffer); + + if( _connectMode != SERVICE_MODE ) + connectServiceMode(); + if( _connectMode != SERVICE_MODE ) + LOG_THROW("Cannot connect OSTC4 service mode..."); + + + unsigned char echo = retryCommand(_serial, 0x88); // 0x88 request all samples (dump) + if( echo != 0x88 ) + LOG_THROW("Bad text reply (1)" << echo); + + _serial.sleep(500); + + for(index = 0; index < 384; index++) // Expect 8 blocks a 0x8000 + { + _serial.readBlock(pBuffer + (index * 0x8000), 0x8000); + LOG_INFO(QString::asprintf("%d",index)); + } + _serial.readBlock(pBuffer + (index * 0x8000), 4); // get lastdiveID + unsigned char ok = _serial.readByte(); + if( ok != 0x4C ) //Service mode only. + LOG_THROW("Bad clock reply (2)"); +} + + +void OSTC4Operations::writeAllSamples(unsigned char* pBuffer) +{ + unsigned short index = 0; + char buffer[60]; + memset(buffer, 0, sizeof buffer); + + if( _connectMode != SERVICE_MODE ) + connectServiceMode(); + if( _connectMode != SERVICE_MODE ) + LOG_THROW("Cannot connect OSTC4 service mode..."); + + + unsigned char echo = retryCommand(_serial, 0x89); // 0x88 request all samples (dump) + if( echo != 0x89 ) + LOG_THROW("Bad text reply (1)" << echo); + + _serial.sleep(500); + + for(index = 0; index < 384; index++) // Expect 8 blocks a 0x8000 384 + { + _serial.writeBlock((const unsigned char*)(pBuffer + (index * 0x8000)), 0x8000); + LOG_INFO(QString::asprintf("%d",index)); + } + _serial.writeBlock(pBuffer + (index * 0x8000), 4); // set lastdiveID + unsigned char ok = _serial.readByte(); + if( ok != 0x4C ) //Service mode only. + LOG_THROW("Bad clock reply (2)"); +} + +//////////////////////////////////////////////////////////////////////////////
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC4Operations.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,107 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC4Operations.h +/// \brief Implementing various operations for H&W OSTC4 dive computer +/// \author JD Gascuel. +/// \sa OSTC3Operations, HardwareOperations.h +/// +/// \copyright (c) 2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2015-09-28 jDG: Creation initial (RadioComm) version. +// 2016-04-25 jDG: Final H&W version. + +#ifndef OSTC4_OPERATIONS_H +#define OSTC4_OPERATIONS_H + +#include "OSTC3Operations.h" +#include <QRegularExpression> + +#include "AES/Adler16.h" +#include "Export.h" +#include <QFile> + +////////////////////////////////////////////////////////////////////////////// +/// \brief Implementing various operations for H&W OSTC4 dive computer +class OSTC4Operations + : public OSTC3Operations +{ + //------------------------------------------------------------------------ + /// \brief Custom text size (lines and columns). + EXPORT QSize nameSize() const override; + + //------------------------------------------------------------------------ + /// \brief Read OSTC4 computer firmware, serial and custom text. + /// Everything is formated for the description() string. + /// \throws if something goes wrong. + EXPORT void getIdentity() override; + + EXPORT QString model() const override; + + /// \brief Tells what is supported for a given computer. + EXPORT CompanionFeatures supported() const override; + + struct FirmwareOSTC4 { + uint length; + uchar type, dummy5, dummy6, dummy7; + uint checksum; + struct { + unsigned char x, y, z, beta; + } version; + }; + + QFile _file; + + /// \param[in] dryRun: If TRUE, do not upload FW, just check file structure. + void openFirmware(const QString& fileName, bool dryRun); + + void loadFirmwarePart(FirmwareOSTC4& header, + QByteArray& bin, int part, + bool dryRun); + + void getSignal() override; + void getAllHeader(unsigned char* pBuffer) override; + void writeAllHeader(unsigned char* pBuffer) override; + void getAllSamples(unsigned char* pBuffer) override; + void writeAllSamples(unsigned char* pBuffer) override; + + EXPORT QString firmwareTemplate() const override; + // EXPORT QRegExp portTemplate() const override; + EXPORT QRegularExpression portTemplate() const override; + EXPORT QStringList listPorts() const override; + EXPORT void upgradeFW(const QString &fileName) override; + + Adler32 fileChecksum; + +public: + EXPORT OSTC4Operations(); +}; + +#endif // OSTC4_OPERATIONS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTCFrogOperations.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,578 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTCFrogOperations.cpp +/// \brief Implementing various operations for H&W Frog dive computer +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "OSTCFrogOperations.h" + +#include "Utils/Log.h" +#include "Utils/Exception.h" + +#include "HexFile.h" +#include "SettingsDialog.h" + +#include <QApplication> +#include <QDateTime> +#include <QProgressBar> +#include <QStringList> + +// Byte extration, compatible littleendian or bigendian. +#define LOW(x) ((unsigned char)((x) % 256)) +#define HIGH(x) ((unsigned char)((x / (1<<8)) % 256)) +#define UPPER(x) ((unsigned char)((x / (1<<16)) % 256)) +#define UP32(x) ((unsigned char)((x / (1<<24)) % 256)) + +#define IMAGE_ROUNDING 1024 +#define FIRMWARE_AREA 0x3E0000 +#define FIRMWARE_SIZE 0x01D000 + +extern QProgressBar* progress; + +////////////////////////////////////////////////////////////////////////////// + +OSTCFrogOperations::OSTCFrogOperations() + : _firmware(0), + _serialNumber(0), + _isOpen(false), + _commandMode(false) +{ +} + +OSTCFrogOperations::~OSTCFrogOperations() +{ + if( _isOpen ) + disconnect(true); +} + +////////////////////////////////////////////////////////////////////////////// +/// /// +/// PORT management /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +//QRegExp OSTCFrogOperations::portTemplate() const +QRegularExpression OSTCFrogOperations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegExp("tty[.]frog.*", Qt::CaseInsensitive); +#elif defined(Q_OS_LINUX) + // Seems ok for debian, ubuntu, and SUSE (google dixit). + // Obviously, needs the rfcomm package. "hcitool scan" or lsusb to have + // a list of connected stuff... + return QRegExp("rfcomm.*", Qt::CaseInsensitive); +#elif defined(Q_OS_WIN) +// return QRegExp("COM.*", Qt::CaseSensitive); + return QRegularExpression( + "COM([0-9]+)", + QRegularExpression::CaseInsensitiveOption + ); +#endif +} + +QStringList OSTCFrogOperations::listPorts() const +{ + return listBluetoothPorts(); +} + +QString OSTCFrogOperations::firmwareTemplate() const +{ + return "*frog.firmware.hex"; +} + +QString OSTCFrogOperations::model() const +{ + return "Frog"; +} + +QString OSTCFrogOperations::description() +{ + return _description; +} + +QImage OSTCFrogOperations::dumpScreen() const +{ + LOG_THROW("Not implemented..."); + return QImage(); +} + +HardwareOperations::CompanionFeatures OSTCFrogOperations::supported() const +{ + // No ICON, no PARAMETER, no FIRMWARE, no DUMPSCREEN yet... + return CompanionFeatures(NAME|DATE); +} + +bool OSTCFrogOperations::connect() +{ + try { + //---- Open the serial port------------------------------------------- + LOG_TRACE( QString("Open port %1...").arg(Settings::port) ); + _isOpen = false; + + //---- Execute the start protocol ------------------------------------ + static char animation[] = "/-\\|"; + + int reply = 0; + for(int retry=0; retry < 30; ++retry) { + _serial.open( Settings::port, "Frog"); + _serial.sleep(100); + _serial.purge(); + _serial.writeByte(0xBB); + try { + reply = _serial.readByte(); + if( reply == 0x4D ) + break; + } + catch(...) {} + LOG_TRACE( QString("Starting... %1").arg(animation[retry % sizeof animation])); + } + if( reply != 0x4D ) + LOG_THROW("Not started"); + + //---- Enquire about Frog id ----------------------------------------- + getIdentity(); + + //---- Everything is ok ---------------------------------------------- + _isOpen = true; + return true; + } + catch(const Exception& e) { + _serial.close(); + LOG_THROW("Cannot connect " << Settings::port << ": " << e.what()); + } + _isOpen = false; + return false; +} + +void OSTCFrogOperations::connectServiceMode() +{ + connect(); +} + +bool OSTCFrogOperations::disconnect(bool /*closing*/) +{ + if( !_isOpen ) return false; + + _serial.purge(); + _serial.writeByte(0xFF); + _serial.sleep(100); + + _serial.purge(); + _serial.close(); + + _description.clear(); // cleanup for interface updateStatus() + _isOpen = false; + + return true; +} + +////////////////////////////////////////////////////////////////////////////// +/// /// +/// LOW Level commands /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::beginCommands() +{ + Q_ASSERT( !_commandMode ); + static char animation[] = "/-\\|"; + for(int i=0;; ++i) + { + if( i == 100 ) // 20.0 sec loop ? + LOG_THROW("Bad reply to open command"); + + _serial.sleep(100); + _serial.purge(); + _serial.writeByte(0xAA); // Start byte + + int reply = 0; + try { + reply = _serial.readByte(); + } catch(...) {} + if( reply == 0x4B ) + goto Started; + + LOG_TRACE(QString("Connecting %1") + .arg(animation[i%4])); + _serial.sleep(200); + continue; + +Started: + unsigned char buffer[] = "\xAA\xAB\xAC"; + _serial.writeBlock(buffer, 3); + + try { + unsigned char reply = _serial.readByte(); + if( reply == 0x4C ) { + _commandMode = true; + return; + } + } catch(...) {} + + _serial.sleep(200); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::endCommands() +{ + Q_ASSERT( _commandMode ); + + _serial.sleep(100); + _serial.purge(); + _serial.writeByte(0xFF); // Exit service mode + _serial.sleep(10); + + unsigned char buffer = _serial.readByte(); + if( buffer != 0xFF ) + LOG_THROW("End failed"); + + _commandMode = false; + disconnect(); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::eraseRange(unsigned int addr, unsigned int size) +{ + Q_ASSERT( _commandMode ); + + // Convert size to number of pages, rounded up. + size = ((size + 4095) / 4096); + if( size < 256 || addr != 0x300000 ) + { + unsigned char buffer[4]; + // Erase just the needed pages. + buffer[0] = UPPER(addr); + buffer[1] = HIGH(addr); + buffer[2] = LOW(addr); + buffer[3] = LOW(size); + _serial.writeByte(0x42); // Command + _serial.sleep(10); + + _serial.writeBlock(buffer, 4); + // Wait (120/4)ms by block of 4K, plus 3% VAT to be sure. + _serial.sleep(40 + size * 31); + } + else + { + // Erase the whole 512KB of icon memory... + _serial.writeByte(0x41); + _serial.sleep(3000); + } + + try { + (void)_serial.readByte(); + } catch(...) {} +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::startWrite(unsigned int addr) +{ + Q_ASSERT( _commandMode ); + + unsigned char buffer[3]; + buffer[0] = UPPER(addr); + buffer[1] = HIGH(addr); + buffer[2] = LOW(addr); + + _serial.writeByte(0x30); + _serial.sleep(10); + + _serial.writeBlock(buffer, 3); + _serial.sleep(10); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::stopWrite() +{ + Q_ASSERT( _commandMode ); + + _serial.flush(); + _serial.sleep(200); // Should be > 100ms. + + unsigned char reply = _serial.readByte(); + if( reply != 0x4C ) + LOG_THROW("stopWrite"); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::readBytes(unsigned int addr, + unsigned char* ptr, + unsigned int size) +{ + Q_ASSERT( _commandMode ); + + unsigned char buffer[6]; + buffer[0] = UPPER(addr); + buffer[1] = HIGH(addr); + buffer[2] = LOW(addr); + buffer[3] = UPPER(size); + buffer[4] = HIGH(size); + buffer[5] = LOW(size); + + _serial.writeByte(0x20); + _serial.sleep(10); + + _serial.writeBlock(buffer, 6); + _serial.sleep(10); + + unsigned int len = _serial.readBlock(ptr, size); + if( len < size ) + LOG_THROW("readBytes too short"); + + unsigned char reply = _serial.readByte(); + if( reply != 0x4C ) + LOG_THROW("readBytes"); +} + +////////////////////////////////////////////////////////////////////////////// +/// /// +/// HIGH Level commands /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::getIdentity() +{ + //---- get model + HardwareDescriptor hw = hardwareDescriptor(); + if( hw != HW_UNKNOWN_OSTC && hw != HW_Frog ) + LOG_THROW("Not a Frog."); + + //---- get identity + _serial.sleep(100); // Make sure last command is finished. + _serial.purge(); + _serial.writeByte('i'); // 0x63 + + unsigned char buffer[1+2+2+13+1] = {0}; + unsigned len = _serial.readBlock(buffer, sizeof buffer); + + if( len != sizeof buffer || buffer[0] != 'i' || buffer[18] != 0x4D ) + LOG_THROW("get identity data"); + + _serialNumber = buffer[1] + buffer[2]*256; + _firmware = buffer[3]*256 + buffer[4]; + + _description = QString("%1 #%2, v%3.%4, %5") + .arg(model()) + .arg(_serialNumber, 4, 10, QChar('0')) + .arg(_firmware / 256).arg(_firmware % 256) + .arg( QString::fromLatin1((char*)buffer+5, 13) + .replace(QChar('\0'), "") + .trimmed() ); + + LOG_TRACE("Found " << _description); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::writeText(const QString& _msg) +{ + // Pad to 15 chars: + QByteArray ascii = (_msg + QString(15, QChar(' '))).left(15).toLatin1(); + + _serial.sleep(100); // Make sure last command is finished. + _serial.purge(); + _serial.writeByte('n'); // 0x6E + + unsigned char reply = _serial.readByte(); + if( reply != 'n' ) + LOG_THROW("message start"); + + _serial.writeBlock((unsigned char *)ascii.constData(), 15); + reply = _serial.readByte(); + if( reply != 0x4D ) + LOG_THROW("message end"); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::setDate(const QDateTime &date) +{ + unsigned char buffer[6]; + buffer[0] = date.time().hour(); + buffer[1] = date.time().minute(); + buffer[2] = date.time().second(); + buffer[3] = date.date().month(); + buffer[4] = date.date().day(); + buffer[5] = date.date().year() % 100; + + _serial.sleep(100); // Make sure last command is finished. + _serial.purge(); + _serial.writeByte('b'); // 0x62 + + unsigned char reply = _serial.readByte(); + if( reply != 'b' ) + LOG_THROW("sync time"); + + _serial. writeBlock( buffer, sizeof buffer); + reply = _serial.readByte(); + if( reply != 0x4D ) + LOG_THROW("sync time end"); + + writeText("Set " + date.toString("MM/dd hh:mm")); +} + +////////////////////////////////////////////////////////////////////////////// + +QSize OSTCFrogOperations::nameSize() const +{ + return QSize(13, 1); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::setName(const QString &newName) +{ + QByteArray padded = (newName+QString(13, QChar(' '))).left(13).toLatin1(); + + _serial.sleep(100); // Make sure last command is finished. + _serial.purge(); + _serial.writeByte('c'); // 0x63 + + unsigned char reply = _serial.readByte(); + if( reply != 'c' ) + LOG_THROW("set custom text"); + + _serial.writeBlock((unsigned char*)padded.constData(), 13); + reply = _serial.readByte(); + if( reply != 0x4D ) + LOG_THROW("custom text end"); + + // Re-read new name: + getIdentity(); + writeText(customText()); +} + +////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::setIcons(const QString &/*fileName*/) +{ + // beginCommands(); + // eraseRange(0x000000, 0x00000); + // startWrite(0x000000); + // stopWrite(); + // endCommands(); + + LOG_THROW( "Set icons: Not yet implemented." ); +} + +int OSTCFrogOperations::firmware() const +{ + return _firmware; +} + +int OSTCFrogOperations::serialNumber() const +{ + return _serialNumber; +} + +QString OSTCFrogOperations::customText() const +{ + return _description.section(',', 2).trimmed(); +} + +/////////////////////////////////////////////////////////////////////////////// + +static unsigned char frogSecretKey[16] = { + 111, 85, 190, 69, + 108,254, 242, 19, + 231, 49, 248,255, + 233, 48, 176,241 +}; + +void OSTCFrogOperations::loadFirmware(HexFile &hex, const QString &fileName) const +{ + hex.allocate(FIRMWARE_SIZE); + hex.loadEncrypted(fileName, frogSecretKey); +} + +/////////////////////////////////////////////////////////////////////////////// + +void OSTCFrogOperations::upgradeFW(const QString &fileName) +{ + try { + //---- Load and check firmware --------------------------------------- + LOG_TRACE("Loading firmware..."); + + HexFile hex; + loadFirmware(hex, fileName); + unsigned int checksum = hex.checksum(); + + beginCommands(); + writeText("Frog Companion"); + getIdentity(); + + unsigned char buffer[5]; + buffer[0] = LOW(checksum); + buffer[1] = HIGH(checksum); + buffer[2] = UPPER(checksum); + buffer[3] = UP32(checksum); + + // Compute magic checksum's checksum. + buffer[4] = 0x55; + buffer[4] ^= buffer[0]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); + buffer[4] ^= buffer[1]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); + buffer[4] ^= buffer[2]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); + buffer[4] ^= buffer[3]; buffer[4] =(buffer[4]<<1 | buffer[4]>>7); + + _serial.sleep(100); // Make sure last command is finished. + _serial.purge(); + _serial.writeByte('P'); // 0x50 + + unsigned char reply = _serial.readByte(); + if( reply != 'P' ) + LOG_THROW( "Programming start" ); + + _serial.writeBlock(buffer, sizeof buffer); + _serial.sleep(4000); + + // NOTE: the device never return, because it always to a reset, + // with ot without reprogramming... + _serial.close(); + _isOpen = false; + } + catch(const Exception& e) { + LOG_TRACE(QString("Cannot upgrade: <font color='red'>%1</font>") + .arg(e.what())); + + // Unknown state: so make a hard cleanup: + _commandMode = false; + _isOpen = false; + _serial.close(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTCFrogOperations.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,94 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTCFrogOperations.h +/// \brief Implementing various operations for H&W Frog dive computer +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2011-10-01 : Separate from FrogTools main.cpp +// 2014-10-23 : Mix with OSTC Companion code. + +#ifndef OSTC_FROG_OPERATIONS_H +#define OSTC_FROG_OPERATIONS_H + +#include "HardwareOperations.h" + +class OSTCFrogOperations + : public HardwareOperations +{ + int _firmware; + int _serialNumber; + QString _description; + bool _isOpen; + bool _commandMode; + + //---- Low level commands + void beginCommands(); + void endCommands(); + void eraseRange(unsigned int addr, unsigned int size); + void startWrite(unsigned int addr); + void stopWrite(); + void readBytes(unsigned int addr, unsigned char *ptr, unsigned int size); + + + //---- Port management + //QRegExp portTemplate() const override; + QRegularExpression portTemplate() const override; + QStringList listPorts() const override; + HardwareOperations::CompanionFeatures supported() const override; + + //---- High level commands reimplemented + bool connect() override; + void connectServiceMode() override; + void getIdentity() override; + void writeText(const QString &_msg) override; + void setDate(const QDateTime& date) override; + QSize nameSize() const override; + void setName(const QString& newName) override; + void setIcons(const QString& fileName) override; + int firmware() const override; + int serialNumber() const override; + QString customText() const override; + void loadFirmware(HexFile& hex, const QString& fileName) const override; + void upgradeFW(const QString& fileName) override; + QString firmwareTemplate() const override; + bool disconnect(bool closing = false) override; + QString model() const override; + QString description() override; + QImage dumpScreen() const override; + +public: + OSTCFrogOperations(); + ~OSTCFrogOperations(); +}; + +#endif // OSTC_FROG_OPERATIONS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTCSportOperations.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,136 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTCSportOperations.cpp +/// \brief Implementing various operations for OSTC3 dive computer +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "OSTCSportOperations.h" + +#include "Utils/Log.h" + +#include <QStringList> +#include <QRegularExpression> + +////////////////////////////////////////////////////////////////////////////// + +OSTCSportOperations::OSTCSportOperations() +{ + emulatorName = "OSTC_Sport"; +} + +////////////////////////////////////////////////////////////////////////////// +#if 0 +QRegExp OSTCSportOperations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegExp("tty.OSTCs.*", Qt::CaseInsensitive); +#elif defined(Q_OS_LINUX) + // Seems ok for debian, ubuntu, and SUSE (google dixit). + // Obviously, needs the rfcomm package. "hcitool scan" or lsusb to have + // a list of connected stuff... + return QRegExp("rfcomm.*", Qt::CaseInsensitive); +#elif defined(Q_OS_WIN) + return QRegExp("COM.*", Qt::CaseSensitive); +#endif +} +#endif +QRegularExpression OSTCSportOperations::portTemplate() const +{ +#if defined(Q_OS_MAC) + return QRegularExpression("tty.OSTCs.*", + QRegularExpression::CaseInsensitiveOption); +#elif defined(Q_OS_LINUX) + // Debian, Ubuntu, SUSE, benötigt rfcomm-Paket + return QRegularExpression("rfcomm.*", + QRegularExpression::CaseInsensitiveOption); +#elif defined(Q_OS_WIN) + return QRegularExpression("COM.*"); // default: case-sensitive +#endif +} +////////////////////////////////////////////////////////////////////////////// + +void OSTCSportOperations::getIdentity() +{ + descriptionString.clear(); + + LOG_TRACE("Getting model..."); + HardwareDescriptor hw = hardwareDescriptor(); + if( hw != HW_UNKNOWN_OSTC && hw != HW_OSTCSport_a && hw != HW_OSTCSport_b ) + LOG_THROW( "Not an OSTC Sport." ); + + LOG_TRACE("Getting identity..."); + getCommonIdentity(); + + // OTC Sport fw is between 10.00 and 19.99 (coded 100x Hi + Lo) + // and serial is between 10.000 and 19.999 + if( hw == HW_UNKNOWN_OSTC + && ( firmware() < 1000 || firmware() > 1999 + || serialNumber() < 10000 || serialNumber() > 20000) ) + LOG_THROW( "Not an OSTC Sport (fw " + << (firmware()/100) << "." << QString::asprintf("%02d", firmware()%100) << ", #" + << serialNumber() << ")."); + hw = HW_OSTCSport_a; + + LOG_TRACE("Found " << descriptionString); +} + +////////////////////////////////////////////////////////////////////////////// + +QStringList OSTCSportOperations::listPorts() const +{ + return listBluetoothPorts(); +} + +////////////////////////////////////////////////////////////////////////////// + +QString OSTCSportOperations::firmwareTemplate() const +{ + return "*_ostc_sport_firmware.hex"; +} + +QSize OSTCSportOperations::nameSize() const +{ + return QSize(12, 5); +} + +QString OSTCSportOperations::model() const +{ + return "OSTCSport"; +} + +HardwareOperations::CompanionFeatures OSTCSportOperations::supported() const +{ + // No ICON, no DUMPSCREEN, no HELIUM, no CCR + return CompanionFeatures(PARAMETERS|DATE|NAME|FIRMWARE + |BLUETOOTH); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTCSportOperations.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,63 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTCSportOperations.h +/// \brief Implementing various operations for H&W OSTC Sport dive computer +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef OSTC_SPORTOPERATIONS_H +#define OSTC_SPORTOPERATIONS_H + +#include "OSTC3Operations.h" + +//#include <QRegExp> +#include <QRegularExpression> + +class OSTCSportOperations + : public OSTC3Operations +{ +// QRegExp portTemplate() const override; + QRegularExpression portTemplate() const override; + QStringList listPorts() const override; + QString firmwareTemplate() const override; + QSize nameSize() const override; + void getIdentity() override; + + QString model() const override; + + CompanionFeatures supported() const override; + +public: + OSTCSportOperations(); +}; + +#endif // OSTC_SPORTOPERATIONS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC_CR_Operations.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,86 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC_CR_Operations.cpp +/// \brief Implementing various operations for H&W OSTC-cR dive computer +/// \author JD Gascuel. +/// \sa ComputerOperations.h +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "OSTC_CR_Operations.h" + +#include "Utils/Log.h" + +////////////////////////////////////////////////////////////////////////////// + +OSTCcROperations::OSTCcROperations() +{ + emulatorName = "OSTC_CR"; +} + +////////////////////////////////////////////////////////////////////////////// + +QString OSTCcROperations::model() const +{ + return "OSTC_cR"; +} + +HardwareOperations::CompanionFeatures OSTCcROperations::supported() const +{ + // No ICON, no DUMPSCREEN + return CompanionFeatures(PARAMETERS|DATE|NAME|FIRMWARE + |HELIUM_DIVE|CCR_DIVE); +} + +void OSTCcROperations::getIdentity() +{ + descriptionString.clear(); + + LOG_TRACE("Getting model..."); + HardwareDescriptor hw = hardwareDescriptor(); + if( hw != HW_UNKNOWN_OSTC && hw != HW_OSTCcR_a && hw != HW_OSTCcR_b ) + LOG_THROW("Not an OSTC cR."); + + LOG_TRACE("Getting identity..."); + getCommonIdentity(); + + if( hw == HW_UNKNOWN_OSTC && (firmware() > 0x0A00 || serialNumber() > 10000) ) + LOG_THROW("Not an OSTC cR"); + + LOG_TRACE("Found " << descriptionString); +} + +////////////////////////////////////////////////////////////////////////////// + +QString OSTCcROperations::firmwareTemplate() const +{ + return "*_ostc3_firmware.hex"; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSTC_CR_Operations.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,67 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file OSTC_CR_Operations.h +/// \brief Implementing various operations for H&W OSTC-cR dive computer +/// \author JD Gascuel. +/// \sa ComputerOperations.h +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2014-11-08 : [jDG] Creation, from OSTC3Operations.h + +#ifndef OSTC3_S8_OPERATIONS_H +#define OSTC3_S8_OPERATIONS_H + +#include "OSTC3Operations.h" + +////////////////////////////////////////////////////////////////////////////// +/// \brief Implementing various low-level operations for OSTC3-S8 dive computer +class OSTCcROperations + : public OSTC3Operations +{ + /// \brief Returns "OSTC_cR" + QString model() const override; + CompanionFeatures supported() const override; + + //------------------------------------------------------------------------ + /// \brief Read OSTC3-S8 computer firmware, serial and custom text. + /// Everything is formated for the description() string. + /// \throws if something goes wrong. + void getIdentity() override; + + QString firmwareTemplate() const override; + + ////////////////////////////////////////////////////////////////////////// +public: + OSTCcROperations(); +}; + +#endif // OSTC3_S8_OPERATIONS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources.qrc Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,19 @@ +<RCC> + <qresource prefix="/"> + <file>Images/Settings.svg</file> + <file>Images/app_OSTC_Companion.svg</file> + <file>Images/frog_160x120.png</file> + <file>Images/ostc_sport_160x120.png</file> + <file>Images/ostc2c_160x120.png</file> + <file>Images/ostc2p_160x120.png</file> + <file>Images/ostc3_160x120.png</file> + <file>Images/ostc3p_160x120.png</file> + <file>Images/ostc_cr_160x120.png</file> + <file>Images/ostc4_160x120.png</file> + <file>Translations/companion_DE.qm</file> + <file>Translations/companion_ES.qm</file> + <file>Translations/companion_FR.qm</file> + <file>Translations/companion_IT.qm</file> + <file>Translations/companion_RU.qm</file> + </qresource> +</RCC>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Serial.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,426 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file Serial.cpp +/// \brief RS232 serial i/o. +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "Serial.h" + +#include "Utils/Log.h" +#include "Utils/Exception.h" + +#include <QString> + +#ifdef WIN32 +# define S_WRITE(p, b, li, lo) WriteFile(p, b, li, &lo, NULL) +# define S_READ(p, b, li, lo) ReadFile(p, b, li, &lo, NULL) +# define S_LEN DWORD +# define S_FLUSH(p) FlushFileBuffers(p) +# define S_PURGE(p) PurgeComm(p, PURGE_RXCLEAR | PURGE_TXCLEAR) +# define S_CLOSE(p) CloseHandle(p) +#else +# include <fcntl.h> +# include <termios.h> +# include <sys/ioctl.h> +# include <unistd.h> + +# define INVALID_HANDLE_VALUE (-1) +# define S_WRITE(p, b, li, lo) (lo = write(p, b, li)) +# define S_READ(p, b, li, lo) (lo = read (p, b, li)) +# define S_LEN ssize_t +# define S_FLUSH(p) tcdrain(p) +# define S_PURGE(p) tcflush(p,TCIOFLUSH) +# define S_CLOSE(p) ::close(p) +#endif + +/////////////////////////////////////////////////////////////////////////////// + +Serial::~Serial() +{} + +/////////////////////////////////////////////////////////////////////////////// + +void Serial::open(const QString& port, const QString &type) +{ + if( _isOpen ) return; + LOG_TRACE("Open " << port << " ..."); + + _hSerial = INVALID_HANDLE_VALUE; + + //------------------------------------------------------------------------ + // Sanity checks. + if( port.isEmpty() ) + LOG_THROW( "Port is not defined." ); + if( type.isEmpty() ) + LOG_THROW( "Port type is not defined." ); + + bool usbMode = type.contains("ostc2c", Qt::CaseInsensitive) + || ( type.contains("ostc3", Qt::CaseInsensitive) + && !type.contains("ostc3p", Qt::CaseInsensitive)) + || type.contains("ostc_cr", Qt::CaseInsensitive); + LOG_TRACE((usbMode ? "Fast USB" : "Slow Bluetooth") << " connection mode."); + + //------------------------------------------------------------------------ +#ifdef Q_OS_WIN + // BUGFIX: COM ports above COM9 are not automatically recognized, + // hence we have to prepend DEVICE NAMESPACE... + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx + QByteArray com = port.toLatin1() + "\0"; + if( !com.startsWith("\\\\.\\") ) + com = "\\\\.\\" + com; + + _hSerial = CreateFileA(com.data(), + GENERIC_READ | GENERIC_WRITE, + 0, // Exclusive access. + NULL, // No security + OPEN_EXISTING, + FILE_ATTRIBUTE_DEVICE | FILE_FLAG_NO_BUFFERING, + 0); + if(_hSerial==INVALID_HANDLE_VALUE) + { + if(GetLastError()==ERROR_FILE_NOT_FOUND) + LOG_THROW( "Unknown port" ); + LOG_THROW( "Unable to open port" ); + } + S_PURGE(_hSerial); + + DCB dcbSerial = {sizeof(dcbSerial), 0}; + if( !GetCommState(_hSerial, &dcbSerial) ) + LOG_THROW( "Unable to get COM port config" ); + + dcbSerial.BaudRate = CBR_115200; + dcbSerial.ByteSize = 8; + dcbSerial.Parity = NOPARITY; + dcbSerial.StopBits = ONESTOPBIT; + dcbSerial.fOutxCtsFlow = DTR_CONTROL_ENABLE; // NO HARDWARE FLOW CONTROL + dcbSerial.fRtsControl = RTS_CONTROL_ENABLE; //RTS_CONTROL_DISABLE; // NO HARDWARE FLOW CONTROL + + if( !SetCommState(_hSerial, &dcbSerial) ) + LOG_THROW( "Unable to set COM port config" ); + + COMMTIMEOUTS timeouts={0}; + if( usbMode ) { + timeouts.ReadTotalTimeoutConstant = 500; // 0.5 sec + timeouts.WriteTotalTimeoutConstant = 1000; // 1.0 sec + } else { + timeouts.ReadTotalTimeoutConstant = 2000; // 2.0 sec timeout. + } + if( !SetCommTimeouts(_hSerial, &timeouts) ) + LOG_THROW( "Unable to configure port" ); + + LOG_TRACE("Connection:"); + LOG_TRACE(" " << dcbSerial.BaudRate << " bauds."); + LOG_TRACE(" " << (int)dcbSerial.ByteSize << " bits, " + << (dcbSerial.Parity ? "+parity, " : "no parity, ") + << (dcbSerial.StopBits ? QString(" +%1").arg(dcbSerial.StopBits) : QString("no")) << " stops bits."); + LOG_TRACE(" CTS is " << (dcbSerial.fOutxCtsFlow ? "ON." : "OFF.")); + LOG_TRACE(" RTS is " << ((dcbSerial.fRtsControl == RTS_CONTROL_HANDSHAKE) ? "ON." + : (dcbSerial.fRtsControl == RTS_CONTROL_ENABLE) ? "FORCED." + : "OFF.")); + LOG_TRACE(" Read timeout " << timeouts.ReadTotalTimeoutConstant << " msec."); + LOG_TRACE(" Write timeout " << timeouts.WriteTotalTimeoutConstant << " msec."); +#endif + + //------------------------------------------------------------------------ +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) + QByteArray p = port.toLatin1(); + if( ! p.startsWith("/dev/") ) + p = "/dev/" + p; + _hSerial = ::open( p.constData(), O_RDWR | O_NOCTTY | O_NONBLOCK, 0); + if( _hSerial<0 ) + LOG_THROW( "Unable to open port " << p); + + if( ioctl(_hSerial, TIOCEXCL) < 0 ) + LOG_THROW( "Port in use" ); + + // Once opened, clearing the O_NONBLOCK flag. + if( fcntl(_hSerial, F_SETFL, 0) < 0 ) + LOG_THROW( "Can't reset non-blocking I/O" ); + + struct termios termios; + tcgetattr(_hSerial, &termios); + + cfmakeraw(&termios); + if( cfsetspeed(&termios, B115200) < 0 ) + LOG_THROW( "Bad port speed" ); + + termios.c_cflag |= CLOCAL| CS8 | CREAD; // No DTR/DSR/DCD, 8bit. + termios.c_cflag &= ~( PARENB | CSTOPB // No parity, one stop bit. + | CCTS_OFLOW | CRTS_IFLOW); // No hardware flow control. + + if( usbMode ) { + // FAST USB: Fix timeout to 0.5 seconde: + termios.c_cc[VMIN] = 0; // Pure timout mode (no char needed). + termios.c_cc[VTIME] = 5; // 0.5s timeout after last received byte. + } else { + // SLOW BLUETOOTH: Fix timeout to 2.0 sec: + termios.c_cc[VMIN] = 0; // Pure timout mode (no char needed). + termios.c_cc[VTIME] = 20; // 2.0sec timeout after last received byte. + } + + if( tcsetattr(_hSerial, TCSANOW, &termios) < 0 ) + LOG_THROW( "Unable to configure port" ); +#endif + + if( _hSerial == INVALID_HANDLE_VALUE ) + LOG_THROW( "Port not open" ); + + _isOpen = true; + LOG_TRACE("Device open successfull."); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Serial::close() +{ + if( !_isOpen ) return; + LOG_TRACE("Device close ..."); + +#ifdef Q_OS_MAC + if( ioctl(_hSerial, TIOCNXCL) < 0 ) + LOG_THROW( "Port in use" ); +#endif + + if( _hSerial == INVALID_HANDLE_VALUE ) + LOG_THROW( "Port not open" ); + S_CLOSE(_hSerial); + + _hSerial = INVALID_HANDLE_VALUE; + _isOpen = false; + LOG_TRACE("Device close successfull."); +} + +/////////////////////////////////////////////////////////////////////////////// + +unsigned char Serial::readByte() const +{ + if( !_isOpen ) + LOG_THROW( "Port not open" ); + + unsigned char byte = 0; + S_LEN len = 0; + S_READ(_hSerial, &byte, 1, len); + if( len != 1 ) + // LOG_THROW_E(ReadTimeout, "< timeout" ); + LOG_THROW("< timeout" ); + + if( isprint(byte) ) + // LOG_DEBUG("< " << QString().sprintf("%02x '%c'", byte, byte) ); + LOG_DEBUG("< " << QString::asprintf("%02x '%c'", byte, byte)); + else + LOG_DEBUG("< " << QString::asprintf("%02x", byte) ); + + return byte; +} + +void Serial::writeByte(unsigned char byte) const +{ + if( !_isOpen ) + LOG_THROW( "Port not open" ); + + if( isprint(byte) ) + LOG_DEBUG("> " << QString::asprintf("%02x '%c'", byte, byte) ); + else + LOG_DEBUG("> " << QString::asprintf("%02x", byte) ); + + S_LEN len = 0; + S_WRITE(_hSerial, &byte, 1, len); + if( len != 1 ) + // LOG_THROW_E(WriteTimeout, "> timeout"); + LOG_THROW("> timeout"); +} + +/////////////////////////////////////////////////////////////////////////////// + +unsigned short Serial::readShort() const +{ + unsigned char lo = readByte(); + unsigned char hi = readByte(); + return hi << 8 | lo; +} + +void Serial::writeShort(unsigned short word) const +{ + unsigned char lo = word & 0xFF; + unsigned char hi = word >> 8; + writeByte(lo); writeByte(hi); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Serial::writeInt24(unsigned int int24) const +{ + unsigned char lo = int24 & 0xFF; + unsigned char hi = (int24 >> 8) & 0xFF; + unsigned char up = (int24 >> 16) & 0xFF; + writeByte(lo); writeByte(hi); writeByte(up); +} + +unsigned int Serial::readInt24() const +{ + unsigned char lo = readByte(); + unsigned char hi = readByte(); + unsigned char up = readByte(); + return up << 16 | hi << 8 | lo; +} + +/////////////////////////////////////////////////////////////////////////////// + +unsigned int Serial::readBlock(unsigned char* ptr, unsigned int size) const +{ + if( !_isOpen ) + LOG_THROW( "Port not open" ); + + unsigned int bytes = 0; + + bool timeout = false; + while( size > 0 ) + { + // Allow up to 1.0sec for each 4K block. + S_LEN todo = (size > 4096) ? 4096 : size; + S_LEN done = 0; + S_READ(_hSerial, ptr+bytes, todo, done); + + if( done == 0 ) { + timeout = true; + break; + } + + size -= done; + bytes += (unsigned int)done; + } + + if( Log::minLevel <= Log::LEVEL_DEBUG ) + { + const int DUMP_LINE = 16; + const int DUMP_MAX = 3*DUMP_LINE; + const unsigned int length = (bytes < DUMP_MAX) ? bytes : DUMP_MAX; + for(unsigned int i=0; i<length; i += DUMP_LINE) + { + LogAction logger(Log::LEVEL_DEBUG, __FILE__ ,__LINE__, "readBlock()"); + logger << "< "; + for(unsigned int j=i; j<bytes && j<(i+DUMP_LINE); ++j) + logger << QString::asprintf("%02x ", ptr[j]); + for(unsigned int j=i; j<bytes && j<(i+DUMP_LINE); ++j) + logger << (isprint( ptr[j] ) + ? ptr[j] + : (unsigned char)'.'); + } + if( length < bytes ) + LOG_DEBUG("< ... " << (bytes-length) << " more bytes."); + } + + if( timeout ) + LOG_THROW_E(ReadTimeout, "< block timeout (missing " << size << " bytes)"); + + return bytes; +} + +/////////////////////////////////////////////////////////////////////////////// + +void Serial::writeBlock(const unsigned char* ptr, unsigned int size) const +{ + if( !_isOpen ) + LOG_THROW( "Port not open" ); + + if( Log::minLevel <= Log::LEVEL_DEBUG ) + { + const int DUMP_LINE = 16; + const int DUMP_MAX = 3*DUMP_LINE; + const unsigned int length = (size < DUMP_MAX) ? size : DUMP_MAX; + for(unsigned int i=0; i<length; i += DUMP_LINE) + { + LogAction logger(Log::LEVEL_DEBUG, __FILE__ ,__LINE__, "writeBlock()"); + logger << "> "; + for(unsigned int j=i; j<size && j<(i+DUMP_LINE); ++j) + logger << QString::asprintf("%02x ", ptr[j]); + for(unsigned int j=i; j<size && j<(i+DUMP_LINE); ++j) + logger << (isprint( ptr[j] ) + ? ptr[j] + : (unsigned char)'.'); + } + if( length < size ) + LOG_DEBUG("> ... " << (size-length) << " more bytes."); + } + + while( size > 0 ) + { + // Allow up to 1.0sec for each 4K block. + S_LEN chunck = (size > 4096) ? 4096 : size; + S_LEN len = 0; + S_WRITE(_hSerial, ptr, chunck, len); + + if( len == 0 ) + LOG_THROW_E(WriteTimeout, "> block timeout"); + + size -= len; + ptr += len; + } + + // Auto-fluh on each write. + flush(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Serial::purge() +{ + if( !_isOpen ) + LOG_THROW( "Port not open" ); + + // Empty incomming buffer + S_PURGE(_hSerial); + + LOG_TRACE("Device purged."); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Serial::flush() const +{ + if( !_isOpen ) + LOG_THROW( "Port not open" ); + + S_FLUSH(_hSerial); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Serial::sleep(int msec) const +{ +#ifdef WIN32 + Sleep(msec); +#else + usleep(msec*1000); +#endif +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Serial.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,99 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file Serial.h +/// \brief RS232 serial i/o. +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2012-09-22 : [jDG] Split from Frog.cpp to OSTC_Planner.cpp +// 2013-03-17 : [jDG] Adapted from OSTC Planner to OSTC Companion. +// 2014-07-07 : [jDG] Cleanups for Subsurface google-summer-of-code. +// 2014-07-25 : [jDG] BSD 2-clause license. + +#ifndef SERIAL_H +#define SERIAL_H + +class QString; + +#include <QtGlobal> // Q_OS_WIN + +#ifdef Q_OS_WIN +#ifndef NOMINMAX +# define NOMINMAX +#endif +//# define NOMINMAX +# include <windows.h> +# define S_HANDLE HANDLE +# undef NOMINMAX +#else +# define S_HANDLE int +#endif + +////////////////////////////////////////////////////////////////////////////// +// \brief RS232 serial i/o +class Serial +{ + S_HANDLE _hSerial; + bool _isOpen; + +public: + inline Serial() + : _hSerial(0), + _isOpen(false) + {} + + virtual ~Serial(); + + virtual void open(const QString &port, const QString& type); + virtual void close(); + + virtual void writeByte(unsigned char byte) const; + virtual unsigned char readByte() const; + + virtual void writeBlock(const unsigned char* ptr, unsigned int size) const; + virtual unsigned int readBlock(unsigned char* ptr, unsigned int size) const; + + virtual void purge(); + virtual void flush() const; + + void writeShort(unsigned short word) const; + unsigned short readShort() const; + + void writeInt24(unsigned int int24) const; + unsigned int readInt24() const; + + void sleep(int msec) const; + + inline bool isOpen() const { return _isOpen; } +}; + +#endif // SERIAL_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Settings.ui Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,361 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Settings</class> + <widget class="QDialog" name="Settings"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>341</width> + <height>293</height> + </rect> + </property> + <property name="windowTitle"> + <string>OSTC Companion settings</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="10" column="0" colspan="3"> + <widget class="Line" name="line_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="6" column="0" colspan="2"> + <widget class="QCheckBox" name="forceFirmwareUpdate"> + <property name="text"> + <string>Force Firmware update</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="portMenu"> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="11" column="0" colspan="3"> + <widget class="QCheckBox" name="autoSetDateTimeCB"> + <property name="toolTip"> + <string>If checked, any configuration upload will also setup date and time.</string> + </property> + <property name="text"> + <string>Auto setup of date && time</string> + </property> + </widget> + </item> + <item row="8" column="0" colspan="2"> + <widget class="QCheckBox" name="forceFontlibUpdate"> + <property name="text"> + <string>Force FontLib update</string> + </property> + </widget> + </item> + <item row="4" column="0" colspan="3"> + <widget class="QLabel" name="noPortLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Ignored" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="languageMenu"> + <property name="currentIndex"> + <number>0</number> + </property> + <property name="insertPolicy"> + <enum>QComboBox::NoInsert</enum> + </property> + <item> + <property name="text"> + <string notr="true">Deutsch</string> + </property> + <property name="icon"> + <iconset> + <normaloff>:/icons/Icons/german.png</normaloff>:/icons/Icons/german.png</iconset> + </property> + </item> + <item> + <property name="text"> + <string notr="true">English</string> + </property> + <property name="icon"> + <iconset> + <normaloff>:/icons/Icons/english.png</normaloff>:/icons/Icons/english.png</iconset> + </property> + </item> + <item> + <property name="text"> + <string notr="true">Español</string> + </property> + <property name="icon"> + <iconset> + <normaloff>:/icons/Icons/spanish.png</normaloff>:/icons/Icons/spanish.png</iconset> + </property> + </item> + <item> + <property name="text"> + <string notr="true">Français</string> + </property> + <property name="icon"> + <iconset> + <normaloff>:/icons/Icons/french.png</normaloff>:/icons/Icons/french.png</iconset> + </property> + </item> + <item> + <property name="text"> + <string notr="true">Italiano</string> + </property> + <property name="icon"> + <iconset> + <normaloff>:/icons/Icons/italian.png</normaloff>:/icons/Icons/italian.png</iconset> + </property> + </item> + <item> + <property name="text"> + <string notr="true">Русский</string> + </property> + <property name="icon"> + <iconset> + <normaloff>:/icons/Icons/russian.png</normaloff>:/icons/Icons/russian.png</iconset> + </property> + </item> + </widget> + </item> + <item row="2" column="2"> + <widget class="QToolButton" name="updatePorts"> + <property name="toolTip"> + <string>Update port list with currently connected USB or Bluetooth devices.</string> + </property> + <property name="text"> + <string notr="true">...</string> + </property> + </widget> + </item> + <item row="15" column="0" colspan="3"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="resetB"> + <property name="text"> + <string>Restore Defaults</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="okB"> + <property name="text"> + <string>OK</string> + </property> + <property name="shortcut"> + <string>Return</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelB"> + <property name="text"> + <string>Cancel</string> + </property> + <property name="shortcut"> + <string>Ctrl+W</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Language:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="7" column="0" colspan="2"> + <widget class="QCheckBox" name="forceRTEUpdate"> + <property name="text"> + <string>Force RTE update</string> + </property> + </widget> + </item> + <item row="5" column="0" colspan="3"> + <widget class="Line" name="line_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Communication port:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="14" column="0" colspan="3"> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="13" column="0" colspan="3"> + <widget class="QCheckBox" name="useFastMode"> + <property name="text"> + <string>Enable fast mode if supported</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>okB</sender> + <signal>clicked()</signal> + <receiver>Settings</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>212</x> + <y>144</y> + </hint> + <hint type="destinationlabel"> + <x>373</x> + <y>100</y> + </hint> + </hints> + </connection> + <connection> + <sender>cancelB</sender> + <signal>clicked()</signal> + <receiver>Settings</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>293</x> + <y>144</y> + </hint> + <hint type="destinationlabel"> + <x>231</x> + <y>98</y> + </hint> + </hints> + </connection> + <connection> + <sender>languageMenu</sender> + <signal>activated(int)</signal> + <receiver>Settings</receiver> + <slot>languageSlot(int)</slot> + <hints> + <hint type="sourcelabel"> + <x>172</x> + <y>25</y> + </hint> + <hint type="destinationlabel"> + <x>235</x> + <y>26</y> + </hint> + </hints> + </connection> + <connection> + <sender>updatePorts</sender> + <signal>clicked()</signal> + <receiver>Settings</receiver> + <slot>updatePortsSlot()</slot> + <hints> + <hint type="sourcelabel"> + <x>295</x> + <y>52</y> + </hint> + <hint type="destinationlabel"> + <x>170</x> + <y>97</y> + </hint> + </hints> + </connection> + <connection> + <sender>resetB</sender> + <signal>clicked()</signal> + <receiver>Settings</receiver> + <slot>resetSettingsSlot()</slot> + <hints> + <hint type="sourcelabel"> + <x>54</x> + <y>135</y> + </hint> + <hint type="destinationlabel"> + <x>170</x> + <y>78</y> + </hint> + </hints> + </connection> + </connections> + <slots> + <slot>languageSlot(int)</slot> + <slot>resetSettingsSlot()</slot> + <slot>updatePortsSlot()</slot> + </slots> +</ui>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SettingsDialog.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,255 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file SettingsDialog.cpp +/// \brief Preference dialog for OSTC Companion. +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2014 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "SettingsDialog.h" +#include "ui_Settings.h" + +#include "MainWindow.h" // Needed to propagare retranslate() + +#include "Utils/Log.h" + +#include <QApplication> +#include <QDialogButtonBox> +#include <QDir> +#include <QLibraryInfo> +#include <QPushButton> +#include <QSettings> +#include <QTranslator> + +#ifdef Q_OS_WIN +# define NOMINMAX 1 +# include <Windows.h> +# undef NOMINMAX +#endif + +#include "Export.h" +#include "HardwareOperations.h" + +QString EXPORT Settings::language = ""; +QString EXPORT Settings::port = ""; +QString EXPORT Settings::currentPath = ""; + +bool EXPORT Settings::autoSetDateTime = true; + +bool EXPORT Settings::forceFirmwareUpdate = false; +bool EXPORT Settings::forceRTEUpdate = false; +bool EXPORT Settings::forceFontlibUpdate = false; + +bool EXPORT Settings::useFastMode = false; + +extern QSettings* settings; + +////////////////////////////////////////////////////////////////////////////// + +Settings::Settings(QWidget* parent, HardwareOperations *op) + : QDialog(parent), + _ui(new Ui::Settings), + _op(op) +{ + _ui->setupUi(this); + reload(this); +} + +Settings::~Settings() +{ + delete _ui; +} + +////////////////////////////////////////////////////////////////////////////// + +void Settings::reload(Settings* dialog) +{ + //---- Restore options from settings ------------------------------------- + language = settings->value("Interface/lang", + QLocale::system().name().right(2) ).toString(); + port = settings->value("OSTC/port", "").toString(); + currentPath = settings->value("OSTC/currentPath", "").toString(); + autoSetDateTime = settings->value("OSTC/autoSetDateTime", true).toBool(); + forceFirmwareUpdate = settings->value("OSTC/forceFirmwareUpdate", false).toBool(); + forceRTEUpdate = settings->value("OSTC/forceRTEUpdate", false).toBool(); + forceFontlibUpdate = settings->value("OSTC/forceFontlibUpdate", false).toBool(); + + useFastMode = settings->value("OSTC/useFastMode", false).toBool(); + + setLanguage(); + + if( !dialog ) + return; + + //---- Update interface -------------------------------------------------- + if( language == "DE" ) dialog->_ui->languageMenu->setCurrentIndex(0); + if( language == "EN" ) dialog->_ui->languageMenu->setCurrentIndex(1); + if( language == "ES" ) dialog->_ui->languageMenu->setCurrentIndex(2); + if( language == "FR" ) dialog->_ui->languageMenu->setCurrentIndex(3); + if( language == "IT" ) dialog->_ui->languageMenu->setCurrentIndex(4); + if( language == "RU" ) dialog->_ui->languageMenu->setCurrentIndex(5); + + dialog->updatePortsSlot(); + + dialog->_ui->autoSetDateTimeCB->setChecked( autoSetDateTime ); + dialog->_ui->forceFirmwareUpdate->setChecked( forceFirmwareUpdate ); + dialog->_ui->forceRTEUpdate->setChecked( forceRTEUpdate ); + dialog->_ui->forceFontlibUpdate->setChecked( forceFontlibUpdate ); + dialog->_ui->useFastMode->setChecked( useFastMode ); + +} + +////////////////////////////////////////////////////////////////////////////// + +void Settings::languageSlot(int i) +{ + switch(i) { + case 0: language = "DE"; break; + case 1: language = "EN"; break; + case 2: language = "ES"; break; + case 3: language = "FR"; break; + case 4: language = "IT"; break; + case 5: language = "RU"; break; + } + setLanguage(); +} + + +void Settings::updatePortsSlot() +{ + //---- search for possible ports ---------------------------------------- + QStringList list; + if( _op ) { // Known driver type ? + list = _op->listPorts(); + +#ifndef Q_OS_LINUX + if( list.isEmpty() ) + _ui->noPortLabel->setText( + QString("<font color='red'>%1</font>: %2 - %3") + .arg(tr("Warning")) + .arg(tr("no port", "USB connection to OSTC not found")) + .arg(tr("Did you installed the %1 driver ?") +#ifdef Q_OS_WIN + .arg("<a href='http://www.ftdichip.com/Drivers/CDM/CDM%20v2.12.00%20WHQL%20Certified.zip'>FTDI VCP</a>"))); +#elif defined(Q_OS_MACX) + .arg("<a href='http://www.ftdichip.com/drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_2_18.dmg'>FTDI VCP</a>"))); +#else + .arg("USB"))); +#endif + else +#endif + _ui->noPortLabel->clear(); + } + + QString myPort = port + " (current)"; + if( ! port.isEmpty() ) + list += myPort; + list.sort(); + + _ui->portMenu->clear(); + _ui->portMenu->addItems(list); + _ui->portMenu->setCurrentText( port.isEmpty() ? "" : myPort ); +} + +////////////////////////////////////////////////////////////////////////////// + +void Settings::setLanguage() +{ + static QTranslator myappTranslator; + if( myappTranslator.load(":/Translations/companion_" + language) ) + qApp->installTranslator(&myappTranslator); +} + +void Settings::changeEvent(QEvent *e) +{ + if( e->type() == QEvent::LanguageChange ) + { + _ui->retranslateUi(this); + + // FIX: also update the warning text... + updatePortsSlot(); + + // FIX: propagate to main windows. + if( MainWindow* main = dynamic_cast<MainWindow*>(parent()) ) + main->retranslate(); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void Settings::accept() +{ + port = _ui->portMenu->currentText().section(" ", 0, 0); + autoSetDateTime = _ui->autoSetDateTimeCB->isChecked(); + forceFirmwareUpdate = _ui->forceFirmwareUpdate->isChecked(); + forceRTEUpdate = _ui->forceRTEUpdate->isChecked(); + forceFontlibUpdate = _ui->forceFontlibUpdate->isChecked(); + + useFastMode = _ui->useFastMode->isChecked(); + save(); + QDialog::accept(); +} + +void Settings::save() +{ + settings->setValue("Interface/lang", language); + settings->setValue("OSTC/port", port); + settings->setValue("OSTC/currentPath", currentPath); + settings->setValue("OSTC/autoSetDateTime", autoSetDateTime); + settings->setValue("OSTC/forceFirmwareUpdate", forceFirmwareUpdate); + settings->setValue("OSTC/forceRTEUpdate", forceRTEUpdate); + settings->setValue("OSTC/forceFontlibUpdate", forceFontlibUpdate); + settings->setValue("OSTC/useFastMode", useFastMode); + settings->sync(); +} + +////////////////////////////////////////////////////////////////////////////// + +void Settings::reject() +{ + reload(this); + if( MainWindow* main = dynamic_cast<MainWindow*>(parent()) ) + main->retranslate(); + QDialog::reject(); +} + +////////////////////////////////////////////////////////////////////////////// + +void Settings::resetSettingsSlot() +{ + //---- Clear everything ------------------------------------------------- + settings->clear(); + settings->sync(); + //---- Set defaults + reload(this); + //---- Search connected ports + updatePortsSlot(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SettingsDialog.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,104 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file SettingsDialog.h +/// \brief Preference dialog for OSTC Companion. +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2014 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2013-03-17 : [jDG] Copyed and adapted from OSTC Planner. +// 2014-07-07 : [jDG] Cleanups for Subsurface google-summer-of-code. +// 2014-07-25 : [jDG] BSD 2-clause license. + +////////////////////////////////////////////////////////////////////////////// +// Import Qt GUI + +namespace Ui { class Settings; } + +#include <QDialog> + +class HardwareOperations; +class QAbstractButton; +class QSettings; + +////////////////////////////////////////////////////////////////////////////// +/// \brief Implement preference dialog for OSTC Companion. +class Settings + : public QDialog +{ + Q_OBJECT + Ui::Settings* _ui; + HardwareOperations* _op; + + void changeEvent(QEvent *e); + +public: + Settings(QWidget* parent, HardwareOperations *op); + ~Settings(); + + static QString language; + static QString port; + static QString currentPath; + + /// Setup date and time when connecting. + static bool autoSetDateTime; + + /// Skip version check to make development easier + static bool forceFirmwareUpdate; + static bool forceRTEUpdate; + static bool forceFontlibUpdate; + static bool useFastMode; + + /// \brief restore preferences from settings + static void reload(Settings *); + + /// \brief Save preferences to settings. + static void save(); + + /// \brief update Qt translators for language. + static void setLanguage(); + +public slots: + /// \brief dynamically change application's translation. + void languageSlot(int i); + + /// \brief redo list of available ports. + void updatePortsSlot(); + + /// \brief save changes to settings. + void accept(); + + /// \brief restore saved preferences. + void reject(); + + /// \brief Reset all settings to default values + void resetSettingsSlot(); +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Translations/companion_DE.ts Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,255 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="de"> +<context> + <name>MainWindow</name> + <message> + <location filename="../MainWindow.ui" line="28"/> + <source>...</source> + <translation>...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="178"/> + <source>Set Date && Time</source> + <translation>Datum und Uhrzeit stellen</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="146"/> + <source>Open or re-open the last selected USB or Bluetooth port (See Preferences). +NOTE: make sure to connect the serial cable, or to set the bluetooth mode first...</source> + <translation>Öffnen Sie den zuletzt gewählten USB oder Bluetooth Port (erneut). Siehe Einstellungen. +Hinweis: Stellen Sie sicher, dass das Kabel angeschlossen bzw. der Bluetooth Mode gestartet ist... </translation> + </message> + <message> + <location filename="../MainWindow.ui" line="161"/> + <source>Connect</source> + <translation>Verbinden</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="173"/> + <source>Set the H&W computer date and time. +Can be automatic at each connect, if asked to in the Preferences. +(Valid once device is connected).</source> + <translation>Einstellen von Datum und Uhrzeit bei HW Computern. +Uhrzeit/Datum wird automatisch gesetzt, wenn in den Einstellungen aktiviert +(Verfügbar, sobald ein Gerät verbunden).</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="185"/> + <source>Change the name displayed on the H&W device. +(Valid once device is connected).</source> + <translation>Benutzerinfo welche im Gerät angezeigt wird. +(Verfügbar, sobald ein Gerät verbunden).</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="189"/> + <source>Set Name...</source> + <translation>Name...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="196"/> + <source>Upload a customization icon. +Only supported on Frog yet. +(Valid once device is connected).</source> + <translation>Nur beim frog verfügbar. +(Verfügbar, sobald ein Gerät verbunden).</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="201"/> + <source>Set Icon...</source> + <translation>Icon hochladen...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="208"/> + <source>Check Signal</source> + <translation>Prüfe Signalqualität</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="215"/> + <source>Ask for a firmware file, and upload it to the H&W device. +Can be done before or after device (re-)connection.</source> + <translation>Auswahl der Firmware Datei und Installation auf HW computer. +(Verfügbar, sobald ein Gerät verbunden).</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="219"/> + <source>Upgrade Firmware...</source> + <translation>Firmware aktualisieren...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="226"/> + <source>Close USB or Bluetooth connection to the device.</source> + <translation>USB oder Bluetooth Verbindung zum Gerät beenden.</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="229"/> + <source>Close</source> + <translation>Schließen</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="252"/> + <source>Quit</source> + <translation>Ende</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="261"/> + <source>H&W dive computer model.</source> + <translation>HW Tauchcomputermodell.</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="25"/> + <source>Open the Preferences menu.</source> + <translation>Einstellungen öffnen.</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="134"/> + <source>&Help</source> + <translation>&Hilfe</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="135"/> + <source>Preferences...</source> + <translation>Einstellungen...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="172"/> + <source>Official web site</source> + <translation>Offizielle Webseite</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="244"/> + <source>%1 selected.</source> + <translation>%1 ausgewählt.</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="293"/> + <location filename="../MainWindow.cpp" line="312"/> + <location filename="../MainWindow.cpp" line="354"/> + <location filename="../MainWindow.cpp" line="427"/> + <location filename="../MainWindow.cpp" line="456"/> + <location filename="../MainWindow.cpp" line="485"/> + <location filename="../MainWindow.cpp" line="521"/> + <source>Error</source> + <translation>Fehler</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="347"/> + <source>Settings date & time...</source> + <translation>Setzte Datum && Uhrzeit...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="366"/> + <source>Settings name...</source> + <translation>Setzte Name...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="438"/> + <source>Settings icons...</source> + <translation>Setze Icons...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="468"/> + <source>Upgrading firmware...</source> + <translation>Firmware wird aktualisiert...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="516"/> + <source>Request Bluetooth signal strength...</source> + <translation>Frage Signalstärke an...</translation> + </message> +</context> +<context> + <name>Settings</name> + <message> + <location filename="../Settings.ui" line="234"/> + <source>Communication port:</source> + <translation>Verbindungsport:</translation> + </message> + <message> + <location filename="../Settings.ui" line="204"/> + <source>Language:</source> + <translation>Sprache:</translation> + </message> + <message> + <location filename="../Settings.ui" line="41"/> + <source>If checked, any configuration upload will also setup date and time.</source> + <translation>Wenn ausgewählt, wird Datum && Uhrzeit automatisch gesetzt.</translation> + </message> + <message> + <location filename="../Settings.ui" line="44"/> + <source>Auto setup of date && time</source> + <translation>Automatisches Einstellen von Datum && Uhrzeit</translation> + </message> + <message> + <location filename="../Settings.ui" line="51"/> + <source>Force FontLib update</source> + <translation>Aktualisierung von Fontlib erzwingen</translation> + </message> + <message> + <location filename="../Settings.ui" line="141"/> + <source>Update port list with currently connected USB or Bluetooth devices.</source> + <translation>Aktualisiere Portliste von USB oder Bluetooth Geräten.</translation> + </message> + <message> + <location filename="../Settings.ui" line="14"/> + <source>OSTC Companion settings</source> + <translation>OSTC Companion Einstellungen</translation> + </message> + <message> + <location filename="../Settings.ui" line="27"/> + <source>Force Firmware update</source> + <translation>Aktualisierung von Firmware erzwingen</translation> + </message> + <message> + <location filename="../Settings.ui" line="156"/> + <source>Restore Defaults</source> + <translation>Werkseinstellungen</translation> + </message> + <message> + <location filename="../Settings.ui" line="176"/> + <source>OK</source> + <translation>OK</translation> + </message> + <message> + <location filename="../Settings.ui" line="179"/> + <source>Return</source> + <translation>Zurück</translation> + </message> + <message> + <location filename="../Settings.ui" line="192"/> + <source>Cancel</source> + <translation>Abbrechen</translation> + </message> + <message> + <location filename="../Settings.ui" line="195"/> + <source>Ctrl+W</source> + <translation>Strg+W</translation> + </message> + <message> + <location filename="../Settings.ui" line="214"/> + <source>Force RTE update</source> + <translation>Aktualisierung von RTE erzwingen</translation> + </message> + <message> + <location filename="../Settings.ui" line="267"/> + <source>Enable fast mode if supported</source> + <translation>Schnelle Aktualisierung aktivieren wenn unterstützt</translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="156"/> + <source>Warning</source> + <translation>Warnung</translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="157"/> + <source>no port</source> + <comment>USB connection to OSTC not found</comment> + <translation>USB Verbindung zum OSTC nicht gefunden</translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="158"/> + <source>Did you installed the %1 driver ?</source> + <translation>Haben Sie den %1 Treiber installiert?</translation> + </message> +</context> +</TS>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Translations/companion_ES.ts Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,251 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="es"> +<context> + <name>MainWindow</name> + <message> + <location filename="../MainWindow.ui" line="28"/> + <source>...</source> + <translation></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="178"/> + <source>Set Date && Time</source> + <translation>Establecer Fecha y Hora</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="146"/> + <source>Open or re-open the last selected USB or Bluetooth port (See Preferences). +NOTE: make sure to connect the serial cable, or to set the bluetooth mode first...</source> + <translation></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="161"/> + <source>Connect</source> + <translation type="unfinished">Conectar</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="173"/> + <source>Set the H&W computer date and time. +Can be automatic at each connect, if asked to in the Preferences. +(Valid once device is connected).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="185"/> + <source>Change the name displayed on the H&W device. +(Valid once device is connected).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="189"/> + <source>Set Name...</source> + <translation>Establecer Nombre...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="196"/> + <source>Upload a customization icon. +Only supported on Frog yet. +(Valid once device is connected).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="201"/> + <source>Set Icon...</source> + <translation>Establecer Icono...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="208"/> + <source>Check Signal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="215"/> + <source>Ask for a firmware file, and upload it to the H&W device. +Can be done before or after device (re-)connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="219"/> + <source>Upgrade Firmware...</source> + <translation>Actializar Firmware...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="226"/> + <source>Close USB or Bluetooth connection to the device.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="229"/> + <source>Close</source> + <translation>Cerrar</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="252"/> + <source>Quit</source> + <translation>Salir</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="261"/> + <source>H&W dive computer model.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="25"/> + <source>Open the Preferences menu.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="134"/> + <source>&Help</source> + <translation>&Ayuda</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="135"/> + <source>Preferences...</source> + <translation>Preferencias...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="172"/> + <source>Official web site</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="244"/> + <source>%1 selected.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="293"/> + <location filename="../MainWindow.cpp" line="312"/> + <location filename="../MainWindow.cpp" line="354"/> + <location filename="../MainWindow.cpp" line="427"/> + <location filename="../MainWindow.cpp" line="456"/> + <location filename="../MainWindow.cpp" line="485"/> + <location filename="../MainWindow.cpp" line="521"/> + <source>Error</source> + <translation>Error</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="347"/> + <source>Settings date & time...</source> + <translation>Ajustes fecha y hora...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="366"/> + <source>Settings name...</source> + <translation>Ajustes nombre...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="438"/> + <source>Settings icons...</source> + <translation>Ajustes iconos...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="468"/> + <source>Upgrading firmware...</source> + <translation>Actualizando firmware...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="516"/> + <source>Request Bluetooth signal strength...</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Settings</name> + <message> + <location filename="../Settings.ui" line="234"/> + <source>Communication port:</source> + <translatorcomment>(to be kept short)</translatorcomment> + <translation>Puerto COM:</translation> + </message> + <message> + <location filename="../Settings.ui" line="204"/> + <source>Language:</source> + <translation>Idioma:</translation> + </message> + <message> + <location filename="../Settings.ui" line="41"/> + <source>If checked, any configuration upload will also setup date and time.</source> + <translation>Si está activado, cualquier actualización de la configuración modificará también la fecha y hora.</translation> + </message> + <message> + <location filename="../Settings.ui" line="44"/> + <source>Auto setup of date && time</source> + <translation>Auto configuracion de fecha y hora</translation> + </message> + <message> + <location filename="../Settings.ui" line="51"/> + <source>Force FontLib update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="141"/> + <source>Update port list with currently connected USB or Bluetooth devices.</source> + <translation>Actualizar la lista de puertos que actualmente tienen conectados dispositivos Bluetooth o USB.</translation> + </message> + <message> + <location filename="../Settings.ui" line="14"/> + <source>OSTC Companion settings</source> + <translation>Ajustes OSTC Companion</translation> + </message> + <message> + <location filename="../Settings.ui" line="27"/> + <source>Force Firmware update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="156"/> + <source>Restore Defaults</source> + <translatorcomment>(to be kept short)</translatorcomment> + <translation>Restablecer config.</translation> + </message> + <message> + <location filename="../Settings.ui" line="176"/> + <source>OK</source> + <translation>Aceptar</translation> + </message> + <message> + <location filename="../Settings.ui" line="179"/> + <source>Return</source> + <translation>Volver</translation> + </message> + <message> + <location filename="../Settings.ui" line="192"/> + <source>Cancel</source> + <translation>Cancelar</translation> + </message> + <message> + <location filename="../Settings.ui" line="195"/> + <source>Ctrl+W</source> + <translation>Ctrl+W</translation> + </message> + <message> + <location filename="../Settings.ui" line="214"/> + <source>Force RTE update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="267"/> + <source>Enable fast mode if supported</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="156"/> + <source>Warning</source> + <translation>Advertencia</translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="157"/> + <source>no port</source> + <comment>USB connection to OSTC not found</comment> + <translation>Puerto no encontrado</translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="158"/> + <source>Did you installed the %1 driver ?</source> + <translation>¿Ha instalado el driver para el dispositivo %1?</translation> + </message> +</context> +</TS>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Translations/companion_FR.ts Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,256 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="fr"> +<context> + <name>MainWindow</name> + <message> + <location filename="../MainWindow.ui" line="28"/> + <source>...</source> + <translation></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="178"/> + <source>Set Date && Time</source> + <translation>Date && Heure</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="146"/> + <source>Open or re-open the last selected USB or Bluetooth port (See Preferences). +NOTE: make sure to connect the serial cable, or to set the bluetooth mode first...</source> + <translation>Ouvre ou ré-ouvre la dernière connection USB ou Bluetooth sélectionnée (Voir les Préférences). +ATTENTION: connecter le cable USB ou activer Bluetooth avant...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="161"/> + <source>Connect</source> + <translation>Connection</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="173"/> + <source>Set the H&W computer date and time. +Can be automatic at each connect, if asked to in the Preferences. +(Valid once device is connected).</source> + <translation>Met à jours la date et l'heure sur l'ordi H&W. +Peut être automatisé à chaque connection, voir les Préférences. +(Valide une fois que l'ordi est connecté).</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="185"/> + <source>Change the name displayed on the H&W device. +(Valid once device is connected).</source> + <translation>Change le nom affiché sur l'ordi H&W. +(Valide une fois que l'ordi est connecté).</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="189"/> + <source>Set Name...</source> + <translation>Nom de l'ordi...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="196"/> + <source>Upload a customization icon. +Only supported on Frog yet. +(Valid once device is connected).</source> + <translation>Configure l'icon affiché sur l'ordi. +Supporté uniquement pour le Frog. +(Valide une fois que l'ordi est connecté).</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="201"/> + <source>Set Icon...</source> + <translation>Image...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="208"/> + <source>Check Signal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="215"/> + <source>Ask for a firmware file, and upload it to the H&W device. +Can be done before or after device (re-)connection.</source> + <translation>Selectionne un fichier de firmware, et l'envois à l'ordi H&W. +(Valide avant ou aprés la connection à l'ordi).</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="219"/> + <source>Upgrade Firmware...</source> + <translation>MàJ du firmware...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="226"/> + <source>Close USB or Bluetooth connection to the device.</source> + <translation>Ferme la connection USB ou Blutooth vers l'ordi.</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="229"/> + <source>Close</source> + <translation>Fermer la connection</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="252"/> + <source>Quit</source> + <translation>Quitter</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="261"/> + <source>H&W dive computer model.</source> + <translation>Modèle d'ordi H&W connecté.</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="25"/> + <source>Open the Preferences menu.</source> + <translation>Ouvre le menu Préférences.</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="134"/> + <source>&Help</source> + <translation>&Aide</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="135"/> + <source>Preferences...</source> + <translation>Préférences...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="172"/> + <source>Official web site</source> + <translation>Site web officiel</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="244"/> + <source>%1 selected.</source> + <translation>%1 sélectionné.</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="293"/> + <location filename="../MainWindow.cpp" line="312"/> + <location filename="../MainWindow.cpp" line="354"/> + <location filename="../MainWindow.cpp" line="427"/> + <location filename="../MainWindow.cpp" line="456"/> + <location filename="../MainWindow.cpp" line="485"/> + <location filename="../MainWindow.cpp" line="521"/> + <source>Error</source> + <translation>Erreur</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="347"/> + <source>Settings date & time...</source> + <translation>Configuration date & heure...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="366"/> + <source>Settings name...</source> + <translation>Configuration du nom...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="438"/> + <source>Settings icons...</source> + <translation>Configuration image...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="468"/> + <source>Upgrading firmware...</source> + <translation>Mise à jourss du firmware...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="516"/> + <source>Request Bluetooth signal strength...</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Settings</name> + <message> + <location filename="../Settings.ui" line="234"/> + <source>Communication port:</source> + <translation>Port comm :</translation> + </message> + <message> + <location filename="../Settings.ui" line="204"/> + <source>Language:</source> + <translation>Langue :</translation> + </message> + <message> + <location filename="../Settings.ui" line="41"/> + <source>If checked, any configuration upload will also setup date and time.</source> + <translation>Met à jours la date & l'heure à chaque connection.</translation> + </message> + <message> + <location filename="../Settings.ui" line="44"/> + <source>Auto setup of date && time</source> + <translation>MàJ automatique date && heure</translation> + </message> + <message> + <location filename="../Settings.ui" line="51"/> + <source>Force FontLib update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="141"/> + <source>Update port list with currently connected USB or Bluetooth devices.</source> + <translation>Cherche les connections USB ou Bluetooth, et met la liste à jours.</translation> + </message> + <message> + <location filename="../Settings.ui" line="14"/> + <source>OSTC Companion settings</source> + <translation>Préférences OSTC Companion</translation> + </message> + <message> + <location filename="../Settings.ui" line="27"/> + <source>Force Firmware update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="156"/> + <source>Restore Defaults</source> + <translation>Paramètres par défaut</translation> + </message> + <message> + <location filename="../Settings.ui" line="176"/> + <source>OK</source> + <translation>OK</translation> + </message> + <message> + <location filename="../Settings.ui" line="179"/> + <source>Return</source> + <translation></translation> + </message> + <message> + <location filename="../Settings.ui" line="192"/> + <source>Cancel</source> + <translation>Annuler</translation> + </message> + <message> + <location filename="../Settings.ui" line="195"/> + <source>Ctrl+W</source> + <translation></translation> + </message> + <message> + <location filename="../Settings.ui" line="214"/> + <source>Force RTE update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="267"/> + <source>Enable fast mode if supported</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="156"/> + <source>Warning</source> + <translation>Attention</translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="157"/> + <source>no port</source> + <comment>USB connection to OSTC not found</comment> + <translation>Pas de port de connection USB visible</translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="158"/> + <source>Did you installed the %1 driver ?</source> + <translation>Est-ce que le driver %1 est installé ?</translation> + </message> +</context> +</TS>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Translations/companion_IT.ts Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,249 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="it"> +<context> + <name>MainWindow</name> + <message> + <location filename="../MainWindow.ui" line="28"/> + <source>...</source> + <translation>...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="178"/> + <source>Set Date && Time</source> + <translation>Imposta Data e Ora</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="146"/> + <source>Open or re-open the last selected USB or Bluetooth port (See Preferences). +NOTE: make sure to connect the serial cable, or to set the bluetooth mode first...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="161"/> + <source>Connect</source> + <translation type="unfinished">Connetti</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="173"/> + <source>Set the H&W computer date and time. +Can be automatic at each connect, if asked to in the Preferences. +(Valid once device is connected).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="185"/> + <source>Change the name displayed on the H&W device. +(Valid once device is connected).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="189"/> + <source>Set Name...</source> + <translation>Imposta Nome...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="196"/> + <source>Upload a customization icon. +Only supported on Frog yet. +(Valid once device is connected).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="201"/> + <source>Set Icon...</source> + <translation>Imposta Logo...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="208"/> + <source>Check Signal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="215"/> + <source>Ask for a firmware file, and upload it to the H&W device. +Can be done before or after device (re-)connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="219"/> + <source>Upgrade Firmware...</source> + <translation>Aggiorna Firmware...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="226"/> + <source>Close USB or Bluetooth connection to the device.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="229"/> + <source>Close</source> + <translation>Chiudi</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="252"/> + <source>Quit</source> + <translation>Esci</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="261"/> + <source>H&W dive computer model.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="25"/> + <source>Open the Preferences menu.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="134"/> + <source>&Help</source> + <translation>&Aiuto</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="135"/> + <source>Preferences...</source> + <translation>Preferenze...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="172"/> + <source>Official web site</source> + <translation>Sito Web Ufficiale</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="244"/> + <source>%1 selected.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="293"/> + <location filename="../MainWindow.cpp" line="312"/> + <location filename="../MainWindow.cpp" line="354"/> + <location filename="../MainWindow.cpp" line="427"/> + <location filename="../MainWindow.cpp" line="456"/> + <location filename="../MainWindow.cpp" line="485"/> + <location filename="../MainWindow.cpp" line="521"/> + <source>Error</source> + <translation>Errore</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="347"/> + <source>Settings date & time...</source> + <translation>Imposto Data e Ora...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="366"/> + <source>Settings name...</source> + <translation>Imposto Nome...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="438"/> + <source>Settings icons...</source> + <translation>Imposto Logo...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="468"/> + <source>Upgrading firmware...</source> + <translation>Aggiorno Firmware...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="516"/> + <source>Request Bluetooth signal strength...</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Settings</name> + <message> + <location filename="../Settings.ui" line="234"/> + <source>Communication port:</source> + <translation>Porta di Connessione:</translation> + </message> + <message> + <location filename="../Settings.ui" line="204"/> + <source>Language:</source> + <translation>Lingua:</translation> + </message> + <message> + <location filename="../Settings.ui" line="41"/> + <source>If checked, any configuration upload will also setup date and time.</source> + <translation>Seleziona per impostare data e ora ad ogni riconfigurazione.</translation> + </message> + <message> + <location filename="../Settings.ui" line="44"/> + <source>Auto setup of date && time</source> + <translation>Sincronizza data e ora automaticamente</translation> + </message> + <message> + <location filename="../Settings.ui" line="51"/> + <source>Force FontLib update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="141"/> + <source>Update port list with currently connected USB or Bluetooth devices.</source> + <translation>Aggiorna la lista dispositivi USB o Bluetooth connessi.</translation> + </message> + <message> + <location filename="../Settings.ui" line="14"/> + <source>OSTC Companion settings</source> + <translation>Impostazioni OSTC Companion</translation> + </message> + <message> + <location filename="../Settings.ui" line="27"/> + <source>Force Firmware update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="156"/> + <source>Restore Defaults</source> + <translation>Ripristina</translation> + </message> + <message> + <location filename="../Settings.ui" line="176"/> + <source>OK</source> + <translation>OK</translation> + </message> + <message> + <location filename="../Settings.ui" line="179"/> + <source>Return</source> + <translation>Indietro</translation> + </message> + <message> + <location filename="../Settings.ui" line="192"/> + <source>Cancel</source> + <translation>Annulla</translation> + </message> + <message> + <location filename="../Settings.ui" line="195"/> + <source>Ctrl+W</source> + <translation>Ctrl+W</translation> + </message> + <message> + <location filename="../Settings.ui" line="214"/> + <source>Force RTE update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="267"/> + <source>Enable fast mode if supported</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="156"/> + <source>Warning</source> + <translation>Attenzione</translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="157"/> + <source>no port</source> + <comment>USB connection to OSTC not found</comment> + <translation>Dispositivo non rilevato</translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="158"/> + <source>Did you installed the %1 driver ?</source> + <translation>Hai installato %1 drivers?</translation> + </message> +</context> +</TS>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Translations/companion_RU.ts Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,252 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="ru"> +<context> + <name>MainWindow</name> + <message> + <location filename="../MainWindow.ui" line="28"/> + <source>...</source> + <translation></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="178"/> + <source>Set Date && Time</source> + <translation>Установить дату && время</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="146"/> + <source>Open or re-open the last selected USB or Bluetooth port (See Preferences). +NOTE: make sure to connect the serial cable, or to set the bluetooth mode first...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="161"/> + <source>Connect</source> + <translation type="unfinished">Подключить</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="173"/> + <source>Set the H&W computer date and time. +Can be automatic at each connect, if asked to in the Preferences. +(Valid once device is connected).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="185"/> + <source>Change the name displayed on the H&W device. +(Valid once device is connected).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="189"/> + <source>Set Name...</source> + <translation>Установить имя...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="196"/> + <source>Upload a customization icon. +Only supported on Frog yet. +(Valid once device is connected).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="201"/> + <source>Set Icon...</source> + <translation>Установить иконку...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="208"/> + <source>Check Signal</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="215"/> + <source>Ask for a firmware file, and upload it to the H&W device. +Can be done before or after device (re-)connection.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="219"/> + <source>Upgrade Firmware...</source> + <translation>Обновить прошивку...</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="226"/> + <source>Close USB or Bluetooth connection to the device.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="229"/> + <source>Close</source> + <translation>Закрыть</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="252"/> + <source>Quit</source> + <translation>Выход</translation> + </message> + <message> + <location filename="../MainWindow.ui" line="261"/> + <source>H&W dive computer model.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.ui" line="25"/> + <source>Open the Preferences menu.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="134"/> + <source>&Help</source> + <translation>&Помощь</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="135"/> + <source>Preferences...</source> + <translation>Настройки...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="172"/> + <source>Official web site</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="244"/> + <source>%1 selected.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="293"/> + <location filename="../MainWindow.cpp" line="312"/> + <location filename="../MainWindow.cpp" line="354"/> + <location filename="../MainWindow.cpp" line="427"/> + <location filename="../MainWindow.cpp" line="456"/> + <location filename="../MainWindow.cpp" line="485"/> + <location filename="../MainWindow.cpp" line="521"/> + <source>Error</source> + <translation>Ошибка</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="347"/> + <source>Settings date & time...</source> + <translation>Установка даты & времени...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="366"/> + <source>Settings name...</source> + <translation>Установка имени...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="438"/> + <source>Settings icons...</source> + <translation>Установка иконок...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="468"/> + <source>Upgrading firmware...</source> + <translation>Обновление прошивки...</translation> + </message> + <message> + <location filename="../MainWindow.cpp" line="516"/> + <source>Request Bluetooth signal strength...</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Settings</name> + <message> + <location filename="../Settings.ui" line="234"/> + <source>Communication port:</source> + <translation>СОМ порт:</translation> + </message> + <message> + <location filename="../Settings.ui" line="204"/> + <source>Language:</source> + <translation>Язык:</translation> + </message> + <message> + <location filename="../Settings.ui" line="41"/> + <source>If checked, any configuration upload will also setup date and time.</source> + <translation>Синхронизировать дату и время при выгрузке конфигурации.</translation> + </message> + <message> + <location filename="../Settings.ui" line="44"/> + <source>Auto setup of date && time</source> + <translation>Автоматически синхронизировать дату && время</translation> + </message> + <message> + <location filename="../Settings.ui" line="51"/> + <source>Force FontLib update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="141"/> + <source>Update port list with currently connected USB or Bluetooth devices.</source> + <translation>Обновить список портов подключения (USB или Bluetooth).</translation> + </message> + <message> + <location filename="../Settings.ui" line="14"/> + <source>OSTC Companion settings</source> + <translation>Настройки OSTC Companion</translation> + </message> + <message> + <location filename="../Settings.ui" line="27"/> + <source>Force Firmware update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="156"/> + <source>Restore Defaults</source> + <translatorcomment>Восстановить настройки по умолчанию</translatorcomment> + <translation>По умолчанию</translation> + </message> + <message> + <location filename="../Settings.ui" line="176"/> + <source>OK</source> + <translation>Ок</translation> + </message> + <message> + <location filename="../Settings.ui" line="179"/> + <source>Return</source> + <translatorcomment>(Key code for accepting new settings)</translatorcomment> + <translation>Return</translation> + </message> + <message> + <location filename="../Settings.ui" line="192"/> + <source>Cancel</source> + <translation>Отмена</translation> + </message> + <message> + <location filename="../Settings.ui" line="195"/> + <source>Ctrl+W</source> + <translatorcomment>(Key code for rejecting new settings)</translatorcomment> + <translation>Ctrl+W</translation> + </message> + <message> + <location filename="../Settings.ui" line="214"/> + <source>Force RTE update</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../Settings.ui" line="267"/> + <source>Enable fast mode if supported</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="156"/> + <source>Warning</source> + <translation>Винимание</translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="157"/> + <source>no port</source> + <comment>USB connection to OSTC not found</comment> + <translation>COM порт не найден</translation> + </message> + <message> + <location filename="../SettingsDialog.cpp" line="158"/> + <source>Did you installed the %1 driver ?</source> + <translation>Установлен ли драйвер порта %1 ?</translation> + </message> +</context> +</TS>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/Exception.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,56 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file Exception.cpp +/// \brief Generic Exception. +/// \author JD Gascuel. +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "Exception.h" + +#include <QString> + +Exception::Exception(const char* msg) +: _msg(strdup(msg)) +{} + +Exception::Exception(const QString& msg) + : _msg( strdup(msg.toUtf8().constData()) ) +{} + +Exception::~Exception() throw() +{ + if( _msg) free((void*)_msg); +} + +const char* Exception::what() const throw() +{ + return _msg; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/Exception.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,82 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file Exception.h +/// \brief Generic Exception. +/// \author JD Gascuel. +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2013-12-21 jDG: Creation. +// 2016-05-24 jDG: BSD-2 version. + +#ifndef EXCEPTION_H +#define EXCEPTION_H + +#include <QString> +#include <exception> + +////////////////////////////////////////////////////////////////////////////// +#define BUILD_OSTC_COMPANION 1 + +#if defined(BUILD_OSTC_COMPANION) +# define EXPORT __declspec(dllexport) +#else +# define EXPORT __declspec(dllimport) +#endif + +class EXPORT Exception + : public std::exception +{ + const char* const _msg; +public: + explicit Exception(const char* msg); + explicit Exception(const QString& msg); + + // Final cleanup. + ~Exception() throw() override; + + /// Message contains, in UTF8 format. + const char* what() const throw() override; +}; + +////////////////////////////////////////////////////////////////////////////// + +#define DEFINE_EXCEPTION(name, parent) \ +struct EXPORT name \ + : public parent \ +{ explicit inline name(const char* msg) : parent(msg) {} \ + explicit inline name(const QString& msg): parent(msg) {} \ +} + +DEFINE_EXCEPTION(Timeout, Exception); + DEFINE_EXCEPTION(ReadTimeout, Timeout); + DEFINE_EXCEPTION(WriteTimeout, Timeout); + +#endif // EXCEPTION_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/Export.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,18 @@ +#ifndef EXPORT_H +#define EXPORT_H + + +#pragma once + +// Auf Windows unterscheidet man zwischen DLL-Export (build der DLL) und DLL-Import (Verwendung der DLL) +#if defined(_WIN32) +#if defined(BUILDING_OSTC_COMPANION) +#define EXPORT __declspec(dllexport) +#else +#define EXPORT __declspec(dllimport) +#endif +#else +#define EXPORT +#endif + +#endif // EXPORT_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/Log.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,281 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file Log.cpp +/// \brief Basic logging tool +/// \author JD Gascuel. +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "Utils/Log.h" + +#include "Utils/Exception.h" +#include "Utils/LogConsole.h" +#include "Utils/LogFile.h" + +#include "Utils/LogAppender.h" + +#include <QDir> +#include <QStringList> +#include <QRegularExpression> + +// 2013-08-30 jDG: Because of Linux compile, +// we need to keep some minimal Qt4.8 compatibility... +#if !defined(NO_WIDGETS) && QT_VERSION > 0x050100 +# include <QWindow> +#endif + +// Will be automaticallt set more verbose by File or Console appender, +// if needed. +Log::Level Log::minLevel = Log::LEVEL_WARNING; +QString Log::_applicationPath; +QString Log::_applicationName; + +////////////////////////////////////////////////////////////////////////////// + +static QtMessageHandler forwarder = 0; +static void Qt5Error(QtMsgType type, const QMessageLogContext &ctx, const QString &msg) +{ + // Spurious error from Qt 5.4.1, when we DO NOT USE any SSL socket... + if( ctx.category && strcmp(ctx.category, "qt.network.ssl") == 0 ) + return; + + switch(type) { + case QtDebugMsg: LogAction(Log::LEVEL_DEBUG, ctx.file, ctx.line, ctx.function) << msg; break; + case QtInfoMsg: LogAction(Log::LEVEL_TRACE, ctx.file, ctx.line, ctx.function) << msg; break; + case QtWarningMsg: LogAction(Log::LEVEL_WARNING, ctx.file, ctx.line, ctx.function) << msg; break; + case QtCriticalMsg: LogAction(Log::LEVEL_ERROR, ctx.file, ctx.line, ctx.function) << msg; break; + case QtFatalMsg: LogAction(Log::LEVEL_ERROR, ctx.file, ctx.line, ctx.function) << msg; break; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void Log::init(int& argc, char* argv[]) +{ + minLevel = Log::LEVEL_WARNING; + + //---- Get the current application name ---------------------------------- + if( argc > 0 && _applicationPath.isEmpty() ) + { + _applicationPath = QDir::current().absoluteFilePath( + QString::fromLatin1(argv[0]) + .replace('\\', '/')); + _applicationName = _applicationPath.section('/', -1); + _applicationPath = _applicationPath.section('/', 0, -2); + +#ifdef Q_OS_WIN32 + if( _applicationName.endsWith(".exe") ) + _applicationName = _applicationName.section('.', 0, -2); +#elif defined(Q_OS_MACX) + if( _applicationPath.endsWith("/MacOS") ) + _applicationPath = _applicationPath.section('/', 0, -2); +#elif defined(Q_OS_LINUX) + // Nothing special, yet. +#else +# error Unknown OS not yet implemented +#endif + + // Pop "/build/kitName" tail added by the QtCreator build process: + if( _applicationPath.section('/', -2, -2) == "build" ) + _applicationPath = _applicationPath.section('/', 0, -3); + if( _applicationPath.section('/', -1, -1) == "build" ) + _applicationPath = _applicationPath.section('/', 0, -2); + + // Pop /debug or /release tail too (Unix, Visual): + // QRegExp tail("[.]?(debug|release)", Qt::CaseInsensitive); + QRegularExpression tail( + R"([.]?(debug|release))", + QRegularExpression::CaseInsensitiveOption + ); + + // if( tail.exactMatch(_applicationPath.section('/', -1, -1)) ) + QRegularExpressionMatch match = + tail.match(_applicationPath.section('/', -1, -1)); + if (match.hasMatch()) + _applicationPath = _applicationPath.section('/', 0, -2); + if( _applicationName.endsWith(" DEBUG", Qt::CaseInsensitive) ) + _applicationName = _applicationName.left(_applicationName.length()-6); + + // Pop "/bin/" tail added by the configured build process: + if( _applicationPath.section('/', -1) == "bin" ) + _applicationPath = _applicationPath.section('/', 0, -2); + + if( _applicationName.isEmpty() ) + _applicationName = "log"; + } + + //---- Forward Qt's messages --------------------------------------------- + forwarder = qInstallMessageHandler(Qt5Error); + + //---- Instanciate 2 appenders ------------------------------------------- + new LogConsole(argc, argv); + new LogFile(argc, argv); +} + +////////////////////////////////////////////////////////////////////////////// + +void Log::close() +{ + LOG_TRACE("Closing logs."); + foreach(LogAppender* i, LogAppender::list()) + delete i; +} + +////////////////////////////////////////////////////////////////////////////// + +Log::Log(Level level, const char *f, int line, const char *function) +: level(level), + line(line), + function(function) +{ + // Skip trailing "../" in file name (due to QtCreator build dir): + while( f && f[0] == '.' && f[1] == '.' && (f[2] == '\\' || f[2] == '/') ) + f += 3; + const_cast<QString&>(file) = QString::fromLatin1(f).replace('\\', '/'); +} + +LogAction::LogAction(Level level, const char *file, int line, const char *function) +: Log(level, file, line, function) +{} + +////////////////////////////////////////////////////////////////////////////// +LogAction::~LogAction() +{ + LogAppender::all(*this); +} + +////////////////////////////////////////////////////////////////////////////// +LogAction &LogAction::operator<<(char msg) +{ + if( message.length() < (LOG_MAX_MESSAGE_LENGTH-1) ) + message += QChar(msg); // UTF-8 by default. + return *this; +} + +LogAction &LogAction::operator<<(unsigned char msg) +{ + if( message.length() < (LOG_MAX_MESSAGE_LENGTH-1) ) + message += QChar(msg); // UTF-8 by default. + return *this; +} + +LogAction& LogAction::operator<<(const char* msg) +{ + if( message.length()+strlen(msg) < (LOG_MAX_MESSAGE_LENGTH) ) + message += QString::fromUtf8(msg); + return *this; +} + +////////////////////////////////////////////////////////////////////////////// +LogAction& LogAction::operator<<(const QString& msg) +{ + if( message.length()+msg.length() < (LOG_MAX_MESSAGE_LENGTH) ) + message += msg; + return *this; +} + +LogAction &LogAction::operator <<(const QStringList& list) +{ + *this << "{"; + for(int i=0; i<list.count(); ++i) + { + if( i > 0 ) + *this << ", "; + *this << list[i]; + } + *this << "}"; + + return *this; +} + + +////////////////////////////////////////////////////////////////////////////// +LogAction& LogAction::operator<<(const QByteArray& msg) +{ + operator<<(QString::fromUtf8(msg)); + return *this; +} + +////////////////////////////////////////////////////////////////////////////// +LogAction& LogAction::operator<<(double value) +{ + operator<<(QString::number(value)); + return *this; +} + +////////////////////////////////////////////////////////////////////////////// +LogAction& LogAction::operator<<(int value) +{ + operator<<(QString::number(value)); + return *this; +} + +LogAction& LogAction::operator<<(unsigned int value) +{ + operator<<(QString::number(value)); + return *this; +} + +////////////////////////////////////////////////////////////////////////////// + +LogAction &LogAction::operator <<(unsigned long value) +{ + operator<<(QString::number((qulonglong)value)); + return *this; +} + +LogAction &LogAction::operator <<(long value) +{ + operator<<(QString::number((qlonglong)value)); + return *this; +} + +////////////////////////////////////////////////////////////////////////////// + +LogAction &LogAction::operator <<(unsigned long long value) +{ + operator<<(QString::number(value)); + return *this; +} + +LogAction &LogAction::operator <<(long long value) +{ + operator<<(QString::number(value)); + return *this; +} + +////////////////////////////////////////////////////////////////////////////// +LogAction &LogAction::operator <<(const void *ptr) +{ + operator<<("0x"); + operator<<(QString::number((qulonglong)ptr, 16)); + + return *this; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/Log.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,251 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file Log.h +/// \brief Basic logging tool +/// \author JD Gascuel. +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2013-07-01 jDG: Creation. +// 2016-05-24 jDG: BSD-2 version. + +#ifndef LOG_H +#define LOG_H + +#include <QString> +#include <QStringList> +#include <QByteArray> + +//class QStringList; +//class QByteArray; + +#define LOG_MAX_MESSAGE_LENGTH 2048 + +////////////////////////////////////////////////////////////////////////////// +/// \brief Basic logging tool +/// +/// +/// Provides easy and centralized log messages for the application. +/// Typical usage: +/// \code +/// main(int argc, char* argv[]) +/// { +/// Log::autoLoad(argc, argv); // Read XML configuration. +/// LOG_TRACE("... "); // Just a tick where we are. +/// +/// LOG_INFO("Main started"); // Visible message (users can see it). +/// LOG_TRACE("Now argc=" << argc); +/// LOG_TRACE("Invisible message, only for log files"); +/// LOG_DEBUG("Invisible, and logged only if run with a "-d" argument"); +/// } +/// \endcode +/// +/// \note that message destination can be multiple, and are the responsability +/// of the LogAppender class hierarchy. +/// +/// \sa LogAppender, LogConsole, LogFile. +class Log +{ + /// Application's reference. + static QString _applicationPath; + static QString _applicationName; + +public: + ////////////////////////////////////////////////////////////////////////// + /// \{ + /// \name Record message and context + + //------------------------------------------------------------------------ + /// \brief Log level: severity scale. + enum Level { + LEVEL_NOERROR = 0, + LEVEL_DEBUG, ///< The most verbose one, used only during debug session. + LEVEL_TRACE, ///< Messages to be put in silent log files, for post-mortem analysis. + LEVEL_INFO, ///< General information send to the final user. + LEVEL_WARNING, ///< Error reported to the user (eg. with a Qt warning box. \sa LogAppenderWindow). + LEVEL_THROW, ///< Error that is mean to be catched, and corrected automatically. + LEVEL_ERROR ///< Fatal error that cannot be recovered. The program should halt. + }; + + //------------------------------------------------------------------------ + const Level level; ///< Event's log level. + const QString file; ///< Source file that fired the log event. + const int line; ///< Source line that fired the log event. + const QString function; ///< Function (and class) that fired the event. + QString message; ///< The log message itself. + + /// \} + ////////////////////////////////////////////////////////////////////////// + /// \{ + /// \name Global services + + //------------------------------------------------------------------------ + /// \brief Minimal error level to log. + /// + /// Defaults to LEVEL_TRACE. + static Level minLevel; + + //------------------------------------------------------------------------ + /// \brief Initialize the log system, by loading some XML config. + /// + /// Might be used to change logging configuration of installed + /// applications. + //------------------------------------------------------------------------ + /// Initialize the log system, by loading some XML config. + static void autoLoad(int argc, char *argv[]); + + //------------------------------------------------------------------------ + /// \brief Initialize the log system, without configurations. + /// + /// Then one can instanciate LogAppender classes in a static way. + static void init(int& argc, char* argv[]); + + //------------------------------------------------------------------------ + /// \brief End the log systems, closing all appenders. + static void close(); + + //------------------------------------------------------------------------ + /// \brief Utility to retrieve where the executable was installed. + static inline QString applicationPath(); + + /// \brief Utility to retrieve the executable's name. + static inline QString applicationName(); + + /// \} + +protected: + /// \brief Constructor + /// + /// All message are to be created by a LogAction instance, responsible + /// to build-up the message, and to send it to LogAppender instances. + Log(Level level, const char *file, int line, const char *function); +}; + +inline QString Log::applicationPath() { return _applicationPath; } +inline QString Log::applicationName() { return _applicationName; } + +////////////////////////////////////////////////////////////////////////////// +/// \brief Creates a Log event. +/// +/// Uses object's destructor to send the Log into all registered log appenders. +class LogAction + : public Log +{ +public: + /// Starts a log line, with timestamp and source-stamp. + LogAction(Level level, const char* file, int line, const char* function); + + /// Finish (and print) a log line. + ~LogAction(); + + /// Appends a single char to the log line. + LogAction& operator<<(char msg); + /// Appends a single char to the log line. + LogAction& operator<<(unsigned char msg); + + /// Appends a C string to the log line. + LogAction& operator<<(const char* msg); + /// Appends a Qt's wide-char string to the log line, converted to Latin1. + LogAction& operator<<(const QString& msg); + /// Appends a list of Qt's wide-char strings. + LogAction& operator<<(const QStringList& msg); + /// Appends a Qt's single-byte-string to the log line. + LogAction& operator<<(const QByteArray& msg); + + /// Appends a float/double to the log line. + LogAction& operator<<(double value); + /// Appends a signed int/short/byte to the log line. + LogAction& operator<<(int value); + /// Appends an unsigned int/short/byte to the log line. + LogAction& operator<<(unsigned int value); + + // Resole ambiguities for the C++ compiler. + /// Appends a signed long to the log line. + LogAction& operator<<(long value); + /// Appends an unsigned long to the log line. + LogAction& operator<<(unsigned long value); + /// Appends a signed long long to the log line. + LogAction& operator<<(long long value); + /// Appends an unsigned long long to the log line. + LogAction& operator<<(unsigned long long value); + + /// Print any pointer as an Hex address: + LogAction& operator<<(const void* ptr); +}; + +#ifndef LOG_FUNCTION_ +# if defined __PRETTY_FUNCTION__ +# define LOG_FUNCTION_ __PRETTY_FUNCTION__ +# elif defined __FUNCSIG__ +# define LOG_FUNCTION_ __FUNCSIG__ +# elif defined __FUNCTION__ +# define LOG_FUNCTION_ __FUNCTION__ +# elif defined __func__ +# define LOG_FUNCTION_ __func__ +# elif defined LINUX +# define LOG_FUNCTION_ __PRETTY_FUNCTION__ +# else +# define LOG_FUNCTION_ __FILE__ +# endif +#endif + +#define LOG_DEBUG(msg) do { if( Log::minLevel <= Log::LEVEL_DEBUG ) LogAction(Log::LEVEL_DEBUG, __FILE__, __LINE__, LOG_FUNCTION_ ) << msg; } while(0) +#define LOG_TRACE(msg) do { if( Log::minLevel <= Log::LEVEL_TRACE ) LogAction(Log::LEVEL_TRACE, __FILE__, __LINE__, LOG_FUNCTION_ ) << msg; } while(0) +#define LOG_INFO(msg) do { if( Log::minLevel <= Log::LEVEL_INFO ) LogAction(Log::LEVEL_INFO, __FILE__, __LINE__, LOG_FUNCTION_ ) << msg; } while(0) +#define LOG_WARNING(msg) do { if( Log::minLevel <= Log::LEVEL_WARNING) LogAction(Log::LEVEL_WARNING, __FILE__, __LINE__, LOG_FUNCTION_ ) << msg; } while(0) +#define LOG_ERROR(msg) do { if( Log::minLevel <= Log::LEVEL_ERROR ) LogAction(Log::LEVEL_ERROR, __FILE__, __LINE__, LOG_FUNCTION_ ) << msg; } while(0) + +#include <sstream> // Für std::ostringstream +#include "Exception.h" + +// Hilfsmakros für stringification +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +#define LOG_THROW_E(EXCEPTION, msg) \ + do { \ + LogAction log(Log::LEVEL_THROW, __FILE__, __LINE__, LOG_FUNCTION_ ); \ + log << msg; \ + throw EXCEPTION(log.message); \ + } while(0) + +#define LOG_THROW(msg) \ + do { \ + LogAction log(Log::LEVEL_THROW, __FILE__, __LINE__, LOG_FUNCTION_ ); \ + log << msg; \ + throw ::Exception(log.message); \ + } while(0) + +#define assert(e) \ + do { if( !(e) ) \ + LOG_THROW("Assert: " #e); \ + } while(0) + +#endif // LOG_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/LogAppender.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,114 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file LogAppender.cpp +/// \brief Put Log message somewhere. +/// \author JD Gascuel. +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "Utils/LogAppender.h" + +#include <QDateTime> +#include <QDir> +#include <QSettings> + +////////////////////////////////////////////////////////////////////////////// +/// List of all currently defined appenders +/// NOTE: Use singleton to avoid messing-up with DLL on windows. + +QList<LogAppender *>& LogAppender::list() +{ + static QList<LogAppender *> appenderList; + return appenderList; +} + +////////////////////////////////////////////////////////////////////////////// +// Add new instance to the list. +// TODO: Lock for threads-safe ? +LogAppender::LogAppender(int, char *[]) + : _minLevel(Log::LEVEL_INFO) +{ + //---- Manage list of all appenders. + list().push_back(this); +} + +////////////////////////////////////////////////////////////////////////////// +// Remove defunct instance from the list. +// TODO: Lock for threads-safe ? +LogAppender::~LogAppender() { + list().removeAll(this); +} + +void LogAppender::setMinLevel(Log::Level level) +{ + _minLevel = level; + if( level < Log::minLevel ) + Log::minLevel = level; +} + +void LogAppender::setFormat(const char *format) +{ + _format = format; +} + +QString LogAppender::format(const Log &log) const +{ + QString line = _format.isEmpty() + ? QString::fromUtf8(defaultFormat()) + : QString::fromUtf8(_format); + + line.replace("%d", QDate::currentDate().toString("yyyy/MM/dd").toLatin1()); + line.replace("%t", QTime::currentTime().toString("hh:mm:ss.zzz").toLatin1()); + line.replace("%o", (log.level == Log::LEVEL_TRACE ) ? "TRACE " : + (log.level == Log::LEVEL_DEBUG ) ? "DEBUG " : + (log.level == Log::LEVEL_INFO ) ? "INFO " : + (log.level == Log::LEVEL_WARNING) ? "WARNING" : + (log.level == Log::LEVEL_THROW ) ? "THROW " : + (log.level == Log::LEVEL_ERROR ) ? "ERROR " : "-------"); + line.replace("%l", QString::number(log.line).toLatin1()); + line.replace("%f", log.file); + line.replace("%F", log.file.section('/', -1)); + line.replace("%p", log.function); + line.replace("%m", log.message); + + return line; +} + +////////////////////////////////////////////////////////////////////////////// +// Send to all instanciated appenders. +// TODO: Lock for threads-safe ? +void LogAppender::all(const Log& log) +{ + foreach(LogAppender* i, list()) + { + if( log.level >= i->_minLevel ) + (*i)(log); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/LogAppender.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,127 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file LogAppender.h +/// \brief Put Log message somewhere. +/// \author JD Gascuel. +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2013-11-15 jDG: Creation. +// 2016-05-24 jDG: BSD-2 version. + +#include "Utils/Log.h" + +#ifndef LOGAPPENDER_H +#define LOGAPPENDER_H + +#include <QByteArray> +#include <QString> + +#include <QList> + +class LogFilter; + +////////////////////////////////////////////////////////////////////////////// + +class EXPORT LogAppender +{ + /// Send a message to this appender. + virtual void operator()(const Log& log) = 0; + + /// Log::close() needs an access to the list, to remove all appenders. + friend void Log::close(); + + /// Manage a list of all existing appenders. + static QList<LogAppender *> &list(); + +protected: + /// How to dislay logged data ? + QByteArray _format; + Log::Level _minLevel; + + /// Default formating string for that appender. + virtual const char* type() const = 0; + virtual const char* defaultFormat() const = 0; + virtual Log::Level defaultMinLevel() const = 0; + + /// Do the substtutions + /// Use the string set by setFormat() to format the \p log event. + QString format(const Log& log) const; + +public: + LogAppender(int argc, char *argv[]); + virtual ~LogAppender(); + + //------------------------------------------------------------------------ + /// Filter messages by error level. + /// Sets the minimum level to use for this appender. + /// \p level is one of: + /// * Log::LEVEL_DEBUG : The most verbose one, used only during debug session. + /// * Log::LEVEL_TRACE : Messages to be put in silent log files, for post-mortem analysis. + /// * Log::LEVEL_INFO : General information send to the final user. + /// * Log::LEVEL_WARNING : Error reported to the user (eg. with a Qt warning box. \sa LogAppenderWindow). + /// * Log::LEVEL_THROW : Error that is mean to be catched, and corrected automatically. + /// * Log::LEVEL_ERROR : Fatal error that cannot be recovered. The program should halt. + /// + /// The default depends from the appender. + void setMinLevel(Log::Level level); + + //------------------------------------------------------------------------ + /// Set current formating. + /// + /// How to format log line once appended. + /// Default depends of the appender used (\sa defaultFormat()). + /// Setting a null string returns to the appender's default. + /// + /// Valide substitutions are: + /// * %o : log level (one of DEBUG, TRACE, INFO, THROW, WARNING, ERROR). + /// * %d : log's date. + /// * %t : log's time. + /// * %f : source file where the error is logged (just trailling filename) + /// * %F : source file where the error is logged (full path) + /// * %l : source line where the error is logged. + /// * %p : the function that fired the log message. + /// * %m : the log message itself. + /// + /// Typical usage: + /// \code + /// LogAppenderMemory mem(0,0); + /// mem.setFormat("[%o] %f:%l %m"); + /// ... + /// LOG_TRACE("Here"); + /// \endcode + void setFormat(const char* format); + + //------------------------------------------------------------------------ + /// Send a message to all existing appenders. + static void all(const Log& log); +}; + +#endif // LOGAPPENDER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/LogConsole.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,144 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file LogConsole.cpp +/// \brief Put Log message onto standard console. +/// \author JD Gascuel. +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "LogConsole.h" + +#include "Log.h" + +//#include <QRegExp> +#include <QRegularExpression> + +#include <stdio.h> + +#ifdef Q_OS_WIN32 +# include <windows.h> +# include <io.h> +# include <fcntl.h> +# include <iostream> +#endif + +////////////////////////////////////////////////////////////////////////////// + +// Nothing to do ... +LogConsole::LogConsole(int argc, char *argv[]) + : LogAppender(argc, argv) +{ + setMinLevel(defaultMinLevel()); + + for(int i=1; i<argc; ++i) + { + if( strcmp("-dd", argv[i]) == 0 ) + setMinLevel(Log::LEVEL_DEBUG); + else if( strcmp("-d", argv[i]) == 0 ) + setMinLevel(Log::LEVEL_TRACE); + } +} + +LogConsole::~LogConsole() +{} + +////////////////////////////////////////////////////////////////////////////// + +const char *LogConsole::type() const +{ + return "Console"; +} + +Log::Level LogConsole::defaultMinLevel() const +{ +#ifdef DEBUG + return Log::LEVEL_TRACE; +#else + return Log::LEVEL_INFO; +#endif +} + +const char *LogConsole::defaultFormat() const +{ + return "%t %m"; +} + +void LogConsole::operator()(const Log& log) +{ + QString msg = format(log); + + // 2016-04-19 jDG: + // Remove bad tags from console output... but keep it nice: +#if 0 + msg.replace("<br>", "\n") + .replace(QRegExp("</?table[^>]*>"), "--------------------------------") + .replace(QRegExp("</tr>(?!\n)"), "\n") + .replace("</td>", "\t"); + + QRegExp re("</?[a-zA-Z]+[^>]*/?>"); + while( msg.contains(re) ) + msg.replace(re, ""); +#endif + msg.replace("<br>", "\n") + .replace(QRegularExpression("</?table[^>]*>", QRegularExpression::CaseInsensitiveOption), + "--------------------------------") + .replace(QRegularExpression("</tr>(?!\n)"), + "\n") + .replace("</td>", "\t"); + + // allgemeiner Tag-Entferner + QRegularExpression re("</?[a-zA-Z]+[^>]*/?>"); + + while (re.match(msg).hasMatch()) { + msg.replace(re, ""); + } + // 2014-03-21 jDG: + // + // In UNIX print everything in UTF-8, so that: + // * junitWrapper get it. + // * it is compatible with Linux console. + // + // In Windows, the console DOES NOT accept UTF_8, we just got giberrish... + // So defaults to Windows UNICODE which is Qt3's ucs2() or Qt4 toWCharArray() + // + // Note: it is correct for most Europe chars, but in corean (TCT example), + // the line is truncated. + +#ifdef WIN32 + wchar_t buffer[LOG_MAX_MESSAGE_LENGTH]; + int len = msg.left(LOG_MAX_MESSAGE_LENGTH-2).toWCharArray(buffer); + buffer[len] = 0; + fprintf(stderr, "%S\n", buffer); +#else + QByteArray utf8 = msg.toUtf8(); + fprintf(stderr, "%s\n", utf8.constData()); +#endif + fflush(stderr); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/LogConsole.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,60 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file LogConsole.h +/// \brief Put Log message onto standard console. +/// \author JD Gascuel. +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2013-11-15 jDG: Creation. +// 2016-05-24 jDG: BSD-2 version. + +#ifndef LOGAPPENDERCONSOLE_H +#define LOGAPPENDERCONSOLE_H + +#include "Utils/LogAppender.h" +class XmlTree; + +////////////////////////////////////////////////////////////////////////////// + +struct EXPORT LogConsole + : public LogAppender +{ + LogConsole(int argc, char *argv[]); + ~LogConsole(); + + const char* type() const override; + Log::Level defaultMinLevel() const override; + const char* defaultFormat() const override; + + void operator()(const Log& log) override; +}; + +#endif // LOGAPPENDERCONSOLE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/LogEvent.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,31 @@ +///////////////////////////////////////////////////////////////////////////// +/// \file LogEvent.cpp +/// \brief User event to pass async messages to the console widget. +/// \author JD Gascuel. +/// \copyright (c) 2015 JD Gascuel. All rights reserved. +/// $Id$ +/////////////////////////////////////////////////////////////////////////////// + +#include "LogEvent.h" +#include <QEvent> + +////////////////////////////////////////////////////////////////////////////// + +QEvent::Type LogEvent::_logEventType = QEvent::None; + +////////////////////////////////////////////////////////////////////////////// + +LogEvent::LogEvent(const QString& message) + : QEvent(_logEventType), + _message(message) +{ +} + +////////////////////////////////////////////////////////////////////////////// + +LogEvent* LogEvent::make(const QString& message) +{ + if( _logEventType == QEvent::None ) + _logEventType = (QEvent::Type) QEvent::registerEventType(); + return new LogEvent(message); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/LogEvent.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,30 @@ +///////////////////////////////////////////////////////////////////////////// +/// \file LogEvent.h +/// \brief User event to pass async messages to the console widget. +/// \author JD Gascuel. +/// \copyright (c) 2015 JD Gascuel. All rights reserved. +/// $Id$ +/////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2015-07-06 : Creation + +#ifndef LOGEVENT_H +#define LOGEVENT_H + +#include <QEvent> +#include <QString> + +class LogEvent : public QEvent +{ + static QEvent::Type _logEventType; + LogEvent(const QString& message); + + QString _message; + +public: + static LogEvent* make(const QString& message); + + inline QString what() const { return _message; } +}; + +#endif // LOGEVENT_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/LogFile.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,193 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file LogFile.cpp +/// \brief Put Log message into a log file. +/// \author JD Gascuel. +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "Utils/LogFile.h" + +#include <QCoreApplication> +#include <QDir> +#include <QMutex> +#include <QSettings> +#include <QRegularExpression> + +#ifndef WIN32 +# include <sys/stat.h> // umask +#else +# include <io.h> // umask +#endif + +static QMutex mutex; + +////////////////////////////////////////////////////////////////////////////// + +LogFile::LogFile(int argc, char *argv[]) + : LogAppender(argc, argv), + _holdFiles(5), + _fileNameFormat("%a-%n.txt"), + _logFile(NULL) +{ + setMinLevel(defaultMinLevel()); + for(int i=1; i<argc; ++i) + { + if( strcmp("-dd", argv[i]) == 0 ) + setMinLevel(Log::LEVEL_DEBUG); + else if( strcmp("-d", argv[i]) == 0 ) + setMinLevel(Log::LEVEL_TRACE); + } +} + +LogFile::~LogFile() +{ + if( _logFile ) + fclose(_logFile); + _logFile = NULL; +} + +const char *LogFile::type() const +{ + return "File"; +} + +Log::Level LogFile::defaultMinLevel() const +{ + return Log::LEVEL_TRACE; +} + + +const char* LogFile::defaultFormat() const +{ + return "[%d %t] %o %F:%l: %m"; +} + +////////////////////////////////////////////////////////////////////////////// + +QString LogFile::filePath(bool pattern) const +{ + QString path = +#ifdef Q_OS_MAC + QString("/tmp") +#else + QDir::tempPath() +#endif + + "/" + + _fileNameFormat; + path.replace("%n", pattern ? "*" : "001") + .replace("%a", Log::applicationName()); + return path; +} + +////////////////////////////////////////////////////////////////////////////// + +void LogFile::cleanup() +{ + QString name = filePath(true); + QDir dir(name.section('/', 0, -2)); + name = name.section('/', -1); + + // ---- Make sure the directory exists, and is writable by everyone. + int oldUMask = umask(0); + if( ! dir.exists() ) + QDir::current().mkdir( dir.absolutePath() ); + + // ---- Then perform the cleanup, if necessary + QFileInfoList list = dir.entryInfoList(QStringList(name), QDir::Files, QDir::Name | QDir::Reversed); + // QRegExp parse(name.replace("*", "([0-9]+)")); + QRegularExpression parse(name.replace("*", "([0-9]+)")); + + for(int i=0; i<list.count(); ++i) + { + QString log = list[i].fileName(); +#if 0 + if( parse.exactMatch(log) ) + { + int num = parse.cap(1).toInt(); + if( num >= _holdFiles ) + dir.remove(log); + else + { + QString newName = _fileNameFormat; + newName.replace("%n", QString().sprintf("%03d", num+1)) + .replace("%a", Log::applicationName()); + dir.rename(log, newName); + } + } +#endif + QRegularExpressionMatch match = parse.match(log); // match erzeugen + if (match.hasMatch()) // entspricht exactMatch + { + int num = match.captured(1).toInt(); // entspricht cap(1) + + if (num >= _holdFiles) + dir.remove(log); + else + { + QString newName = _fileNameFormat; + // newName.replace("%n", QString().sprintf("%03d", num+1)) + // .replace("%a", Log::applicationName()); + QString number = QString::number(num + 1).rightJustified(3, '0'); + newName.replace("%n", number) + .replace("%a", Log::applicationName()); + dir.rename(log, newName); + } + } + } + + umask(oldUMask); +} + +////////////////////////////////////////////////////////////////////////////// + +void LogFile::operator()(const Log &log) +{ + // stdio FILE is not reentrant. So a lock here is mandatory. + QMutexLocker locker(&mutex); + + //---- Open the current log file ----------------------------------------- + if( ! _logFile ) + { + //---- Clean old log files + cleanup(); + //---- Then open the new one. + QString name = filePath(); + _logFile = fopen(name.toLocal8Bit().constData(), "at+"); + } + + //---- Output log line --------------------------------------------------- + if( _logFile ) // File open succeded ? + { + QString line = format(log); + fprintf(_logFile, "%s\n", line.toUtf8().constData()); + fflush(_logFile); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/LogFile.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,68 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file LogFile.h +/// \brief Put Log message into a log file. +/// \author JD Gascuel. +/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2013-11-15 jDG: Creation. +// 2016-05-24 jDG: BSD-2 version. + +#ifndef LOGAPPENDERROLLINGFILE_H +#define LOGAPPENDERROLLINGFILE_H + +#include "Utils/LogAppender.h" +#include <stdio.h> + +class QSettings; + +////////////////////////////////////////////////////////////////////////////// +class EXPORT LogFile + : public LogAppender +{ + int _holdFiles; + QString _fileNameFormat; + FILE* _logFile; + +public: + LogFile(int argc, char *argv[]); + ~LogFile() override; + + virtual QString filePath(bool pattern = false) const; + virtual void cleanup(); + + const char* type() const override; + void operator ()(const Log &log) override; + const char *defaultFormat() const override; + Log::Level defaultMinLevel() const override; +}; + +#endif // LOGAPPENDERROLLINGFILE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/ProgressEvent.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,86 @@ +///////////////////////////////////////////////////////////////////////////// +/// \file ProgressEvent.cpp +/// \brief User event to pass async progress-bar updates to the main window. +/// \author JD Gascuel. +/// \copyright (c) 2015 JD Gascuel. All rights reserved. +/// $Id$ +/////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// + +#include "Utils/ProgressEvent.h" + +#include "Utils/Log.h" + +#include <QCoreApplication> + +/////////////////////////////////////////////////////////////////////////////// + +QEvent::Type ProgressEvent::_logEventType = QEvent::None; + +/////////////////////////////////////////////////////////////////////////////// + +ProgressEvent::ProgressEvent(int current, int range) + : QEvent(_logEventType), + current(current), + maximum(range) +{} + +/////////////////////////////////////////////////////////////////////////////// + +ProgressEvent* ProgressEvent::make(int current, int range) +{ + if( _logEventType == QEvent::None ) + _logEventType = (QEvent::Type) QEvent::registerEventType(); + return new ProgressEvent(current, range); +} + +/////////////////////////////////////////////////////////////////////////////// + +ProgressManager::ProgressManager() + : _main(0) +{} + +ProgressManager* ProgressManager::getInstance() +{ + static ProgressManager manager; + return & manager; +} + +void ProgressManager::setMainWindow(QObject* main) +{ + _main = main; + assert(main); +} + +void ProgressManager::post(int current, int range) +{ + assert(_main); + qApp->postEvent(_main, ProgressEvent::make(current, range)); + qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 10); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils/ProgressEvent.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,87 @@ +///////////////////////////////////////////////////////////////////////////// +/// \file ProgressEvent.h +/// \brief User event to pass async progress-bar updates to the main window. +/// \author JD Gascuel. +/// \copyright (c) 2015 JD Gascuel. All rights reserved. +/// $Id$ +/////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2015-07-14 jDG: Creation. +// 2016-05-24 jDG: BSD-2 version. + +#ifndef PROGRESS_EVENT_H +#define PROGRESS_EVENT_H + +#include <QEvent> + +/////////////////////////////////////////////////////////////////////////////// + +class ProgressEvent : public QEvent +{ + static QEvent::Type _logEventType; + + ProgressEvent(int current, int maximum); + +public: + const int current; + const int maximum; + + static ProgressEvent* make(int current, int maximum); +}; + +/////////////////////////////////////////////////////////////////////////////// + +class ProgressManager +{ + QObject* _main; + +public: + static ProgressManager* getInstance(); + + ProgressManager(); + + /// \brief Define who manage ProgressEvent events. + void setMainWindow(QObject* main); + + /// \brief Send (async) progress updates to the main progress bar. + /// Use ProgressEvent class to post updates and resets to the progress + /// bar, displayed by the main thread. + /// When \a current > \a range, this is a reset... + void post(int current = 101, int range = 100); +}; + +#define PROGRESS(current, range) \ + ProgressManager::getInstance()->post(current, range) +#define PROGRESS_THROTTLE() \ + ProgressManager::getInstance()->post(0, 0) +#define PROGRESS_RESET() \ + ProgressManager::getInstance()->post() + +#endif // PROGRESS_EVENT_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/VERSION.txt Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,4 @@ +MAJOR=3 +MINOR=3 +PATCH=1 +BETA=0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/editlogdialog.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,413 @@ +#include "editlogdialog.h" +#include "ui_LogEditor.h" +#include "MainWindow.h" // Needed to propagare retranslate() +#include "Utils/Log.h" + +#include "HardwareOperations.h" + +#include <QApplication> +#include <QDialogButtonBox> +#include <QDir> +#include <QLibraryInfo> +#include <QPushButton> +#include <QTranslator> +#include <QTableWidget> + + +#ifdef Q_OS_WIN +# define NOMINMAX 1 +# include <Windows.h> +# undef NOMINMAX +#endif +#define HEADER2OFFSET 0x400 + +EditLogDialog::EditLogDialog(QWidget* parent, HardwareOperations *op) + : QDialog(parent), + _ui(new Ui::editLogWnd), + _op(op) +{ + uint32_t index, index2; + uint32_t sizeY = 0; + _ui->setupUi(this); + QTableWidget* headerView = _ui->SectorView; + QTableWidget* sampleView = _ui->SampleView; + + _ui->textBrowser_2->setTabStopDistance( + QFontMetricsF(_ui->textBrowser_2->font()).horizontalAdvance(' ') * 4 + ); + headerView->horizontalHeader()->setMinimumSectionSize(8); + headerView->verticalHeader()->setMinimumSectionSize(8); + headerView->horizontalHeader()->setDefaultSectionSize(8); + headerView->verticalHeader()->setDefaultSectionSize(8); + headerView->setColumnWidth(0,8); + headerView->setRowHeight(0,8); + headerView->horizontalHeader()->hide(); + headerView->verticalHeader()->hide(); + + sampleView->horizontalHeader()->setMinimumSectionSize(8); + sampleView->verticalHeader()->setMinimumSectionSize(8); + sampleView->horizontalHeader()->setDefaultSectionSize(8); + sampleView->verticalHeader()->setDefaultSectionSize(8); + sampleView->setColumnWidth(0,8); + sampleView->setRowHeight(0,8); + sampleView->horizontalHeader()->hide(); + sampleView->verticalHeader()->hide(); + + headerView->setRowCount(16); + headerView->setColumnCount(16); + + sampleView->setRowCount(12); + sampleView->setColumnCount(16); + + + HeaderBuffer = new unsigned char[0x8000*8 + 1]; // 64k Headerbuffer + lastDiveindex + SampleBuffer = new unsigned char[0xC00000 + 4]; // 12MB Samplebuffer + nextSampleAddr + + headerView->resize(8*16+2,8*16+2); + sampleView->resize(8*16+2,8*12+2); + sizeY = (uint32_t)(headerView->geometry().width()) / 16; + + if(sizeY < 8) + { + sizeY = 8; + } + + for(index = 0; index <16; index++) + { + headerView->setColumnWidth(index,sizeY); + headerView->setRowHeight(index,sizeY); + for(index2 = 0; index2 < 16; index2++) + { + + item[index * 16 + index2] = new QTableWidgetItem(" "); + headerView->setItem(index, index2, item[index * 16 + index2]); + } + } + + for(index = 0; index <12; index++) + { + sampleView->setColumnWidth(index,sizeY); + sampleView->setRowHeight(index,sizeY); + for(index2 = 0; index2 < 16; index2++) + { + + sampleitem[index * 16 + index2] = new QTableWidgetItem(" "); + sampleView->setItem(index, index2, sampleitem[index * 16 + index2]); + } + } + + headerView->show(); + headerView->setShowGrid(true); + + sampleView->show(); + sampleView->setShowGrid(true); + +} + +EditLogDialog::~EditLogDialog() +{ + uint32_t index; + + for(index = 0; index <256; index++) + { + delete item[index]; + } + for(index = 0; index <192; index++) + { + delete sampleitem[index]; + } + delete _ui; + delete HeaderBuffer; + delete SampleBuffer; +} + + +void EditLogDialog::on_WriteAllHeader_clicked() +{ + _op->writeAllHeader(HeaderBuffer); +} + +void EditLogDialog::on_pushButton_clicked() +{ + Q_ASSERT( _op ); + + HeaderBuffer[0] = 0; + + try { + LOG_INFO(tr("Request All Headers...")); + _op->getAllHeader(HeaderBuffer); + } + catch(const std::exception& e) { + LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") + .arg(tr("Error")) + .arg(e.what()) ); + } + if(HeaderBuffer[0] != 0) + { + LOG_INFO(tr("Got something")); + updateHeaderStatus(); + } +} + + + +void EditLogDialog::on_HeaderUsage_valueChanged(int value) +{ + +} + +void EditLogDialog::on_ReadAllSamples_clicked() +{ + try { + LOG_INFO(tr("Request All Samples...")); + _op->getAllSamples(SampleBuffer); + } + catch(const std::exception& e) { + LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") + .arg(tr("Error")) + .arg(e.what()) ); + } +} + +void EditLogDialog::on_WriteAllSamples_clicked() +{ + try { + LOG_INFO(tr("Request All Samples...")); + _op->writeAllSamples(SampleBuffer); + } + catch(const std::exception& e) { + LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") + .arg(tr("Error")) + .arg(e.what()) ); + } +} + +void EditLogDialog::on_pushButton_2_clicked() +{ + qint64 length; + QFile dumpFile; + dumpFile.setFileName("Log_Dump.bin"); + if( ! dumpFile.open(QIODevice::WriteOnly) ) + { + LOG_THROW( "Cannot create dump file " ); + } + else + { + length = 0x8000*8 + 1; + dumpFile.write((const char*)HeaderBuffer,length); + length = 0xC00000 + 4; + dumpFile.write((const char*)SampleBuffer,length); + dumpFile.close(); + } +} + +void EditLogDialog::on_LoadDump_clicked() +{ + qint64 length; + QFile dumpFile; + dumpFile.setFileName("Log_Dump.bin"); + if( ! dumpFile.open(QIODevice::ReadOnly) ) + { + LOG_THROW( "Cannot read dump file " ); + } + else + { + length = 0x8000*8 + 1; + dumpFile.read((char*)HeaderBuffer,length); + length = 0xC00000 + 4; + dumpFile.read((char*)SampleBuffer,length); + dumpFile.close(); + + updateHeaderStatus(); + updateSampleStatus(); + } +} + +void EditLogDialog::updateHeaderStatus() +{ + QProgressBar* w = _ui->HeaderUsage; + QTableWidget* sv = _ui->SectorView; + SLogbookHeader* LogInfo; + SSmallHeader* smallHeader; + unsigned char HeaderInUse = 0; + uint32_t index, index2; + uint32_t sampleStartAddr, sampleEndAddr, sampleLength; + int row; + int colum; + + for(index = 0; index < 16; index++) + { + for(index2 = 0; index2 < 16; index2++) + { + if((HeaderBuffer[(0x800 * (index * 16 + index2 )) + HEADER2OFFSET] == 0xFA) + && (HeaderBuffer[(0x800 * (index * 16 + index2 )) + HEADER2OFFSET + 1] == 0xFA)) + { + HeaderInUse++; + LogInfo = (SLogbookHeader*)(HeaderBuffer+(0x800 * (index*16+index2))+0x400); + sampleEndAddr = (LogInfo->profileLength[2]<<16) + (LogInfo->profileLength[1]<<8) + LogInfo->profileLength[0]; + if( sampleEndAddr == 0) + { + sv->item(index,index2)->setBackground(Qt::black); + } + else + { + LogInfo = (SLogbookHeader*)(HeaderBuffer+(0x800 * (index*16+index2))); + sampleStartAddr = (LogInfo->pBeginProfileData[2]<<16) + (LogInfo->pBeginProfileData[1]<<8) + LogInfo->pBeginProfileData[0]; + smallHeader = (SSmallHeader*) &SampleBuffer[sampleStartAddr-0x100000]; + sampleLength = (smallHeader->profileLength[2] << 16)+ (smallHeader->profileLength[1] << 8) + smallHeader->profileLength[0]; + if(sampleLength == sampleEndAddr) // - sampleStartAddr)) + { + sv->item(index,index2)->setBackground(Qt::green); + } + else { + sv->item(index,index2)->setBackground(Qt::red); + } + } + + } + else + { + sv->item(index,index2)->setBackground(Qt::white); + } + } + } + row =(HeaderBuffer[(0x8000 * 8)])/16; + colum =(HeaderBuffer[(0x8000 * 8)] % 16); + sv->item(row,colum)->setBackground(Qt::blue); + w->setMaximum(256); + w->setValue(HeaderInUse); +} + + + + + +void EditLogDialog::on_SectorView_cellClicked(int row, int column) +{ + +} + +void EditLogDialog::updateSampleStatus() +{ + uint8_t row,colum; + uint32_t index; + QTableWidget* sv = _ui->SampleView; + QProgressBar* w = _ui->SampleUsage; + uint8_t SamplesInUse = 0; + + for(index = 0; index < 192; index++) + { + row = index / 16; + colum =index % 16; + + if(SampleBuffer[index * 0x10000] != 0xFF) /* used */ + { + SamplesInUse++; + if(SampleBuffer[index * 0x10000 + 0xFFFF] == 0xFF) /* open */ + { + sv->item(row,colum)->setBackground(Qt::blue); + } + else + { + sv->item(row,colum)->setBackground(Qt::green); /* closed */ + } + } + else + { + sv->item(row,colum)->setBackground(Qt::white); /* empty */ + } + } + w->setMaximum(192); + w->setValue(SamplesInUse); +} + + + +void EditLogDialog::on_SectorView_currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn) +{ + SLogbookHeader* LogInfo; + SLogbookHeader* LogInfo2nd; + QTextEdit* tf = _ui->textBrowser_2; + QString InfoText; + QTableWidget* sv = _ui->SampleView; + uint32_t sampleAddrStart = 0; + uint32_t sampleAddrEnd = 0; + uint8_t rowidx, columidx; + + tf->setReadOnly(true); + + LogInfo = (SLogbookHeader*)(HeaderBuffer+(0x800 * (currentRow*16+currentColumn))); + LogInfo2nd = (SLogbookHeader*)(HeaderBuffer+(0x800 * (currentRow*16+currentColumn)) +0x400); + + updateSampleStatus(); + + if(LogInfo->diveHeaderStart == 0xFAFA) + { + sampleAddrStart = (LogInfo->pBeginProfileData[2]<<16) + (LogInfo->pBeginProfileData[1]<<8) + LogInfo->pBeginProfileData[0]; + sampleAddrEnd = (LogInfo2nd->pEndProfileData[2]<<16) + (LogInfo2nd->pEndProfileData[1]<<8) + LogInfo2nd->pEndProfileData[0]; + + // InfoText.sprintf("Header: %d \nNummer: %d\nSamplestart 0x%x\nSampleend 0x%x", currentRow*16+currentColumn, LogInfo->diveNumber,sampleAddrStart,sampleAddrEnd); + InfoText = QString::asprintf( + "Header: %d \nNummer: %d\nSamplestart 0x%x\nSampleend 0x%x", + currentRow*16 + currentColumn, + LogInfo->diveNumber, + sampleAddrStart, + sampleAddrEnd + ); + sampleAddrStart = (LogInfo->pBeginProfileData[2]<<16) + (LogInfo->pBeginProfileData[1]<<8) + LogInfo->pBeginProfileData[0]; + if(sampleAddrStart != 0) + { + sampleAddrStart -= 0x100000; /* substract memory offset */ + sampleAddrStart /= 0x10000; /* calc sector */ + sv->item(sampleAddrStart / 16, sampleAddrStart % 16)->setBackground(Qt::magenta); + } + else + { + sv->item(0, 0)->setBackground(Qt::black); + } + } + else + { + InfoText = QString::asprintf("Empty"); + } + tf->setPlainText((InfoText)); +} + + + + + +void EditLogDialog::on_SampleView_currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn) +{ + uint16_t sampleSector; + uint8_t headerRow, headerColumn; + SLogbookHeader* LogInfo; + SLogbookHeader* LogInfo2nd; + uint32_t sectorStart, sectorEnd; + uint32_t sampleAddrStart, sampleAddrEnd; + QTableWidget* headerView = _ui->SectorView; + + sampleSector = currentRow * 16 + currentColumn; + sectorStart = sampleSector * 0x10000 + 0x100000; + sectorEnd = sectorStart +0xFFFF; + if(SampleBuffer[sectorStart - 0x100000] != 0xFF) // is buffer used? + { + updateHeaderStatus(); + for(headerRow = 0; headerRow < 16; headerRow++) + { + for(headerColumn = 0; headerColumn < 16; headerColumn++) + { + LogInfo = (SLogbookHeader*)(HeaderBuffer+(0x800 * (headerRow*16+headerColumn))); + LogInfo2nd = (SLogbookHeader*)(HeaderBuffer+(0x800 * (headerRow*16+headerColumn)) +0x400); + sampleAddrStart = (LogInfo->pBeginProfileData[2]<<16) + (LogInfo->pBeginProfileData[1]<<8) + LogInfo->pBeginProfileData[0]; + sampleAddrEnd = (LogInfo2nd->pEndProfileData[2]<<16) + (LogInfo2nd->pEndProfileData[1]<<8) + LogInfo2nd->pEndProfileData[0]; + + if(((sampleAddrStart >= sectorStart)&&(sampleAddrStart < sectorEnd) + || (sampleAddrEnd >= sectorStart)&&(sampleAddrEnd < sectorEnd))) + { + headerView->item(headerRow, headerColumn)->setBackground(Qt::magenta); + } + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/editlogdialog.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,182 @@ +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef EDITLOGDIALOG_H +#define EDITLOGDIALOG_H + +#define NUM_GAS (5) /* number of selectable gases */ + +typedef struct +{ + unsigned char profileLength[3]; + unsigned char samplingRate_seconds; + unsigned char numDivisors; + unsigned char tempType; + unsigned char tempLength; + unsigned char tempDivisor; + unsigned char deco_ndlType; + unsigned char deco_ndlLength; + unsigned char deco_ndlDivisor; + unsigned char gfType; + unsigned char gfLength; + unsigned char gfDivisor; + unsigned char ppo2Type; + unsigned char ppo2Length; + unsigned char ppo2Divisor; + unsigned char decoplanType; + unsigned char decoplanLength; + unsigned char decoplanDivisor; + unsigned char cnsType; + unsigned char cnsLength; + unsigned char cnsDivisor; + unsigned char tankType; + unsigned char tankLength; + unsigned char tankDivisor; +} SSmallHeader; + +typedef struct{ +unsigned char active:1; +unsigned char first:1; +unsigned char deco:1; +unsigned char travel:1; +unsigned char senderCode:4; +} gasubit8_t; + +typedef union{ +gasubit8_t ub; +unsigned char uw; +} gasbit8_Type; +typedef struct +{ + unsigned char setpoint_cbar; + unsigned char depth_meter; +} SSetpointLog; + +typedef struct +{ + unsigned char oxygen_percentage; + unsigned char helium_percentage; + unsigned char depth_meter; + gasbit8_Type note; +} SGasListLog; +//Logbook +typedef struct +{ + unsigned short diveHeaderStart; + unsigned char pBeginProfileData[3]; + unsigned char pEndProfileData[3]; + unsigned char profileLength[3]; + unsigned char logbookProfileVersion; + unsigned char dateYear; + unsigned char dateMonth; + unsigned char dateDay; + unsigned char timeHour; + unsigned char timeMinute; + unsigned char extraPagesWithData; /* from here on: changes in order with respect to OSTC3 */ + unsigned short maxDepth; + unsigned short diveTimeMinutes; + unsigned char diveTimeSeconds; + unsigned char samplingRate; + short minTemp; + unsigned short surfacePressure_mbar; + unsigned short desaturationTime; + SGasListLog gasordil[NUM_GAS]; + unsigned char firmwareVersionLow; + unsigned char firmwareVersionHigh; + unsigned short batteryVoltage; + unsigned short cnsAtBeginning; + unsigned char gfAtBeginning; + unsigned char gfAtEnd; + unsigned short personalDiveCount; + SSetpointLog setpoint[NUM_GAS]; + unsigned short maxCNS; + unsigned short averageDepth_mbar; + unsigned short total_diveTime_seconds; + unsigned char salinity; + unsigned char gfLow_or_Vpm_conservatism; + unsigned char gfHigh; + unsigned char decoModel; + float n2Compartments[16]; + float heCompartments[16]; + unsigned char n2CompartDesatTime_min[16]; + unsigned char heCompartDesatTime_min[16]; + unsigned short diveNumber; + unsigned char lastDecostop_m; + unsigned char CCRmode; + unsigned char diveMode; + unsigned char hwHudLastStatus; /* from here on identical to OSTC3 again */ + unsigned short hwHudBattery_mV; + unsigned char batteryGaugeRegisters[6]; + unsigned short diveHeaderEnd; +} SLogbookHeader; + + +namespace Ui { class editLogWnd; } + +#include <QDialog> +#include <QTableWidgetItem> + +class HardwareOperations; + + + +class EditLogDialog + : public QDialog +{ + Q_OBJECT + Ui::editLogWnd* _ui; + HardwareOperations* _op; + + unsigned char* HeaderBuffer; + unsigned char* SampleBuffer; + QTableWidgetItem* item[256]; + QTableWidgetItem* sampleitem[192]; + +public: + EditLogDialog(QWidget* parent, HardwareOperations *op); + ~EditLogDialog(); +private slots: + + void on_WriteAllHeader_clicked(); + void on_pushButton_clicked(); + void on_HeaderUsage_valueChanged(int value); + void on_ReadAllSamples_clicked(); + void on_pushButton_2_clicked(); + + void updateHeaderStatus(); + void updateSampleStatus(); + void on_LoadDump_clicked(); + void on_WriteAllSamples_clicked(); + void on_SectorView_cellClicked(int row, int column); + + + void on_SectorView_currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn); + + void on_SampleView_currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn); +}; + +#endif // EDITLOGDIALOG_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/icon.rc Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,1 @@ +IDI_ICON1 ICON "Images/app_OSTC_Companion.ico"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,144 @@ +////////////////////////////////////////////////////////////////////////////// +/// \file main.cpp +/// \brief Wrap everything up. +/// \author JD Gascuel. +/// +/// \copyright (c) 2011-2014 JD Gascuel. All rights reserved. +/// $Id$ +////////////////////////////////////////////////////////////////////////////// +// +// BSD 2-Clause License: +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// HISTORY +// 2013-03-17 : [jDG] Initial version. +// 2014-07-07 : [jDG] Cleanups for Subsurface google-summer-of-code. +// 2014-07-25 : [jDG] BSD 2-clause license. +// 2014-10-21 : [jDG] Update for Qt5.3 +// 2015-03-16 : [jDG] Update for Qt5.4.1, Renamed OSTC2c, Added OSTC2, OSTC3+ + +#include "MainWindow.h" + +#include "SettingsDialog.h" +#include "Utils/Log.h" + +#include <QApplication> +#include <QSettings> +#include <QSysInfo> +#include <QOperatingSystemVersion> + +QSettings* settings = NULL; + +////////////////////////////////////////////////////////////////////////////// + +int main(int argc, char *argv[]) +{ + //---- LOG system and initialization ------------------------------------- + Log::init(argc, argv); + +#if 0 + LOG_TRACE("OSTC Companion " + << "v" << MAJOR_VERSION << "." << MINOR_VERSION + << (BETA_VERSION ? QString(" beta %1").arg(PATCH_VERSION) : " release") + << " " << QSysInfo::WordSize << "bits" + << " (build " BUILD_VERSION ")."); +#endif +#if 0 +#define LOG_TRACE(msg) \ + do { \ + if(Log::minLevel <= Log::LEVEL_TRACE) { \ + std::ostringstream oss; \ + oss << msg; \ + LogAction(Log::LEVEL_TRACE, __FILE__, __LINE__, LOG_FUNCTION_) << oss.str(); \ + } \ + } while(0) +#endif +#define LOG_TRACE(msg) \ + do { \ + if(Log::minLevel <= Log::LEVEL_TRACE) { \ + LogAction(Log::LEVEL_TRACE, __FILE__, __LINE__, LOG_FUNCTION_) << QString("%1").arg(msg); \ + } \ + } while(0) + +#ifdef Q_OS_WIN32 + + auto os = QOperatingSystemVersion::current(); + + if (os >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10)) { + LOG_TRACE(" Windows 10 or newer"); + } else if (os >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 8)) { + LOG_TRACE(" Windows 8"); + } + +#if 0 + switch(QSysInfo::windowsVersion()) { + case QSysInfo::WV_XP: LOG_TRACE(" Windows XP"); break; + case QSysInfo::WV_2003: LOG_TRACE(" Windows 2003"); break; + case QSysInfo::WV_VISTA: LOG_TRACE(" Windows Vista"); break; + case QSysInfo::WV_WINDOWS7: LOG_TRACE(" Windows 7"); break; + case QSysInfo::WV_WINDOWS8: LOG_TRACE(" Windows 8"); break; + case QSysInfo::WV_WINDOWS8_1: LOG_TRACE(" Windows 8.1"); break; +#if QT_VERSION < 0x0505 + case 0x00c0: +#else + case QSysInfo::WV_WINDOWS10: +#endif + LOG_TRACE(" Windows 10"); break; + default: LOG_TRACE(" Windows " << QString().sprintf("%04X", QSysInfo::windowsVersion()) ); break; + } +#endif +#elif defined(Q_OS_MACX) + if( QSysInfo::macVersion() < 2 ) + LOG_TRACE(" MacOS 9" ); + else + LOG_TRACE(" MacOS 10." << int(QSysInfo::macVersion()-2)); +#elif defined(Q_OS_LINUX) + LOG_TRACE(" Linux "); +#else +# error Unknown OS not yet implemented +#endif + // LOG_TRACE(" Qt build " << QT_VERSION_STR << " (DLL " << qVersion() << ")"); + LOG_TRACE(QString(" Qt build %1 (DLL %2)").arg(QT_VERSION_STR).arg(qVersion())); + //---- Initialize interface ---------------------------------------------- + // Allow nice display on 4K screens: + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); + + QApplication a(argc, argv); + + settings = new QSettings("OSTC", "Companion", &a); + Settings::reload(0); + MainWindow w; + w.show(); + + int rc = a.exec(); + + settings->sync(); + delete settings; + + Log::close(); + return rc; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/o3pack.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,113 @@ +#include <QApplication> +#include <QDir> +#include <QProgressBar> +#include <QProgressDialog> +#include <QtDebug> + +#include "HexFile.h" + +////////////////////////////////////////////////////////////////////////////// +static unsigned char frogSecretKey[16] = { + 111, 85, 190, 69, + 108,254, 242, 19, + 231, 49, 248,255, + 233, 48, 176,241 +}; +static unsigned char ostc3SecretKey[16] = { + 241,233, 176, 48, + 69,111, 190, 85, + 255,231, 248, 49, + 19,108, 242,254 +}; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + app.processEvents(); + + //---- Parse parameters -------------------------------------------------- + QString in, out; + QDir current; + bool force = false; + bool ostc3 = true; + + if( argc < 2 ) goto Usage; + + if( QString(argv[1]).toLower() == "-frog" ) { + ostc3 = false; + argc--, argv++; + } else if( QString(argv[1]).toLower() == "-ostc3" ) { + ostc3 = true; + argc--, argv++; + } + + in = QDir::cleanPath(current.absoluteFilePath(argv[1])); + out = QDir::cleanPath(current.absoluteFilePath(argv[2])); + if( argv[2] == QString("-f") && argc >= 3 ) { + out = QDir::cleanPath(current.absoluteFilePath(argv[3])); + force = true; + } + + //---- Check parameters consistency -------------------------------------- + { + QFileInfo fi(in); + if( ! fi.exists() || ! fi.isReadable() ) { + qWarning().nospace() << "Cannot read input file " << in; + goto Usage; + } + } + + { + QFileInfo fo(out); + if( fo.exists() ) { + if( !force ) { + qWarning().nospace() << "File " << out << " exists. Use -f to force overwrite."; + goto Usage; + } + + if( !fo.isWritable() ) { + qWarning().nospace() << "Cannot write to " << out << ". Protected file ?"; + goto Usage; + } + current.remove(out); + } + } + + //---- Load the HEX file ------------------------------------------------- + { + QProgressBar* progress = new QProgressBar(0); + progress->setFormat("Loading %p%"); + progress->show(); + + try { + HexFile hex; + hex.allocate(0x20000); + + hex.load(in, progress); + hex.sqwiz(0x1E000); + + char sum[10]; + sprintf(sum, "%08X", hex.checksum()); + qDebug() << "Checksum " << sum; + + //---- Save encrypted HEX file ------------------------------------------- + progress->setFormat("Saving %p%"); + hex.saveEncrypted(out, (ostc3 ? ostc3SecretKey : frogSecretKey), progress); + } + catch(const char* msg) { + qWarning() << "Failed: " << msg; + } + + delete progress; + } + + //---- End -------------------------------------------------------------- + return 0; + +Usage: + qWarning() << "Usage:" << endl + << " " << QString(app.arguments()[0]).section('/', -1).section('\\',-1) + << "[-frog|-ostc3]" + << " input.hex [-f] output.hex"; + exit(-1); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui_LogEditor - Kopie.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,152 @@ +/******************************************************************************** +** Form generated from reading UI file 'LogEditor.ui' +** +** Created by: Qt User Interface Compiler version 5.13.0 +** +** WARNING! All changes made in this file will be lost when recompiling UI file! +********************************************************************************/ + +#ifndef UI_LOGEDITOR_H +#define UI_LOGEDITOR_H + +#include <QtCore/QVariant> +#include <QtWidgets/QApplication> +#include <QtWidgets/QDialog> +#include <QtWidgets/QFrame> +#include <QtWidgets/QHeaderView> +#include <QtWidgets/QLabel> +#include <QtWidgets/QProgressBar> +#include <QtWidgets/QPushButton> +#include <QtWidgets/QTableWidget> +#include <QtWidgets/QTextBrowser> + +QT_BEGIN_NAMESPACE + +class Ui_editLogWnd +{ +public: + QLabel *noPortLabel; + QFrame *line_4; + QFrame *line; + QPushButton *WriteAllHeader; + QPushButton *okB; + QPushButton *ReadAllSamples; + QPushButton *WriteAllSamples; + QProgressBar *HeaderUsage; + QProgressBar *SampleUsage; + QPushButton *pushButton; + QTableWidget *SectorView; + QPushButton *pushButton_2; + QPushButton *LoadDump; + QLabel *label; + QTextBrowser *textBrowser_2; + QTableWidget *SampleView; + QPushButton *ResetSampleAddr; + + void setupUi(QDialog *editLogWnd) + { + if (editLogWnd->objectName().isEmpty()) + editLogWnd->setObjectName(QString::fromUtf8("editLogWnd")); + editLogWnd->resize(853, 385); + noPortLabel = new QLabel(editLogWnd); + noPortLabel->setObjectName(QString::fromUtf8("noPortLabel")); + noPortLabel->setGeometry(QRect(9, 34, 16, 16)); + QSizePolicy sizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(noPortLabel->sizePolicy().hasHeightForWidth()); + noPortLabel->setSizePolicy(sizePolicy); + noPortLabel->setTextFormat(Qt::RichText); + noPortLabel->setAlignment(Qt::AlignCenter); + noPortLabel->setOpenExternalLinks(true); + line_4 = new QFrame(editLogWnd); + line_4->setObjectName(QString::fromUtf8("line_4")); + line_4->setGeometry(QRect(20, 150, 341, 16)); + line_4->setFrameShape(QFrame::HLine); + line_4->setFrameShadow(QFrame::Sunken); + line = new QFrame(editLogWnd); + line->setObjectName(QString::fromUtf8("line")); + line->setGeometry(QRect(20, 300, 341, 20)); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + WriteAllHeader = new QPushButton(editLogWnd); + WriteAllHeader->setObjectName(QString::fromUtf8("WriteAllHeader")); + WriteAllHeader->setGeometry(QRect(30, 80, 101, 23)); + okB = new QPushButton(editLogWnd); + okB->setObjectName(QString::fromUtf8("okB")); + okB->setGeometry(QRect(240, 330, 75, 23)); + okB->setAutoDefault(false); + ReadAllSamples = new QPushButton(editLogWnd); + ReadAllSamples->setObjectName(QString::fromUtf8("ReadAllSamples")); + ReadAllSamples->setGeometry(QRect(30, 230, 101, 23)); + WriteAllSamples = new QPushButton(editLogWnd); + WriteAllSamples->setObjectName(QString::fromUtf8("WriteAllSamples")); + WriteAllSamples->setGeometry(QRect(30, 260, 101, 23)); + HeaderUsage = new QProgressBar(editLogWnd); + HeaderUsage->setObjectName(QString::fromUtf8("HeaderUsage")); + HeaderUsage->setGeometry(QRect(30, 20, 131, 23)); + HeaderUsage->setValue(24); + SampleUsage = new QProgressBar(editLogWnd); + SampleUsage->setObjectName(QString::fromUtf8("SampleUsage")); + SampleUsage->setGeometry(QRect(30, 190, 131, 23)); + SampleUsage->setValue(24); + pushButton = new QPushButton(editLogWnd); + pushButton->setObjectName(QString::fromUtf8("pushButton")); + pushButton->setGeometry(QRect(30, 50, 101, 23)); + SectorView = new QTableWidget(editLogWnd); + SectorView->setObjectName(QString::fromUtf8("SectorView")); + SectorView->setGeometry(QRect(180, 10, 151, 131)); + pushButton_2 = new QPushButton(editLogWnd); + pushButton_2->setObjectName(QString::fromUtf8("pushButton_2")); + pushButton_2->setGeometry(QRect(110, 330, 75, 23)); + LoadDump = new QPushButton(editLogWnd); + LoadDump->setObjectName(QString::fromUtf8("LoadDump")); + LoadDump->setGeometry(QRect(20, 330, 75, 23)); + label = new QLabel(editLogWnd); + label->setObjectName(QString::fromUtf8("label")); + label->setGeometry(QRect(520, 10, 47, 13)); + textBrowser_2 = new QTextBrowser(editLogWnd); + textBrowser_2->setObjectName(QString::fromUtf8("textBrowser_2")); + textBrowser_2->setGeometry(QRect(570, 10, 256, 361)); + SampleView = new QTableWidget(editLogWnd); + SampleView->setObjectName(QString::fromUtf8("SampleView")); + SampleView->setGeometry(QRect(180, 180, 171, 41)); + ResetSampleAddr = new QPushButton(editLogWnd); + ResetSampleAddr->setObjectName(QString::fromUtf8("ResetSampleAddr")); + ResetSampleAddr->setGeometry(QRect(340, 10, 101, 23)); + + retranslateUi(editLogWnd); + QObject::connect(okB, SIGNAL(clicked()), editLogWnd, SLOT(accept())); + + okB->setDefault(true); + + + QMetaObject::connectSlotsByName(editLogWnd); + } // setupUi + + void retranslateUi(QDialog *editLogWnd) + { + editLogWnd->setWindowTitle(QCoreApplication::translate("editLogWnd", "OSTC Companion settings", nullptr)); + WriteAllHeader->setText(QCoreApplication::translate("editLogWnd", "WriteAllHeader", nullptr)); + okB->setText(QCoreApplication::translate("editLogWnd", "OK", nullptr)); +#if QT_CONFIG(shortcut) + okB->setShortcut(QCoreApplication::translate("editLogWnd", "Return", nullptr)); +#endif // QT_CONFIG(shortcut) + ReadAllSamples->setText(QCoreApplication::translate("editLogWnd", "ReadAllSamples", nullptr)); + WriteAllSamples->setText(QCoreApplication::translate("editLogWnd", "WriteAllSamples", nullptr)); + pushButton->setText(QCoreApplication::translate("editLogWnd", "ReadAllHeader", nullptr)); + pushButton_2->setText(QCoreApplication::translate("editLogWnd", "Save Dump", nullptr)); + LoadDump->setText(QCoreApplication::translate("editLogWnd", "Load Dump", nullptr)); + label->setText(QCoreApplication::translate("editLogWnd", "Info", nullptr)); + ResetSampleAddr->setText(QCoreApplication::translate("editLogWnd", "ResetSampleAddr", nullptr)); + } // retranslateUi + +}; + +namespace Ui { + class editLogWnd: public Ui_editLogWnd {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // UI_LOGEDITOR_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui_LogEditor.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,140 @@ +/******************************************************************************** +** Form generated from reading UI file 'LogEditor.ui' +** +** Created by: Qt User Interface Compiler version 6.10.1 +** +** WARNING! All changes made in this file will be lost when recompiling UI file! +********************************************************************************/ + +#ifndef UI_LOGEDITOR_H +#define UI_LOGEDITOR_H + +#include <QtCore/QVariant> +#include <QtWidgets/QApplication> +#include <QtWidgets/QDialog> +#include <QtWidgets/QHeaderView> +#include <QtWidgets/QLabel> +#include <QtWidgets/QProgressBar> +#include <QtWidgets/QPushButton> +#include <QtWidgets/QTableWidget> +#include <QtWidgets/QTextBrowser> + +QT_BEGIN_NAMESPACE + +class Ui_editLogWnd +{ +public: + QLabel *noPortLabel; + QPushButton *WriteAllHeader; + QPushButton *okB; + QPushButton *ReadAllSamples; + QPushButton *WriteAllSamples; + QProgressBar *HeaderUsage; + QProgressBar *SampleUsage; + QPushButton *pushButton; + QTableWidget *SectorView; + QPushButton *pushButton_2; + QPushButton *LoadDump; + QLabel *label; + QTextBrowser *textBrowser_2; + QTableWidget *SampleView; + QPushButton *ResetSampleAddr; + + void setupUi(QDialog *editLogWnd) + { + if (editLogWnd->objectName().isEmpty()) + editLogWnd->setObjectName("editLogWnd"); + editLogWnd->resize(853, 385); + noPortLabel = new QLabel(editLogWnd); + noPortLabel->setObjectName("noPortLabel"); + noPortLabel->setGeometry(QRect(9, 34, 16, 16)); + QSizePolicy sizePolicy(QSizePolicy::Policy::Ignored, QSizePolicy::Policy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(noPortLabel->sizePolicy().hasHeightForWidth()); + noPortLabel->setSizePolicy(sizePolicy); + noPortLabel->setTextFormat(Qt::TextFormat::RichText); + noPortLabel->setAlignment(Qt::AlignmentFlag::AlignCenter); + noPortLabel->setOpenExternalLinks(true); + WriteAllHeader = new QPushButton(editLogWnd); + WriteAllHeader->setObjectName("WriteAllHeader"); + WriteAllHeader->setGeometry(QRect(30, 80, 101, 23)); + okB = new QPushButton(editLogWnd); + okB->setObjectName("okB"); + okB->setGeometry(QRect(220, 330, 75, 23)); + okB->setAutoDefault(false); + ReadAllSamples = new QPushButton(editLogWnd); + ReadAllSamples->setObjectName("ReadAllSamples"); + ReadAllSamples->setGeometry(QRect(30, 230, 101, 23)); + WriteAllSamples = new QPushButton(editLogWnd); + WriteAllSamples->setObjectName("WriteAllSamples"); + WriteAllSamples->setGeometry(QRect(30, 260, 101, 23)); + HeaderUsage = new QProgressBar(editLogWnd); + HeaderUsage->setObjectName("HeaderUsage"); + HeaderUsage->setGeometry(QRect(30, 20, 131, 23)); + HeaderUsage->setValue(24); + SampleUsage = new QProgressBar(editLogWnd); + SampleUsage->setObjectName("SampleUsage"); + SampleUsage->setGeometry(QRect(30, 190, 131, 23)); + SampleUsage->setValue(24); + pushButton = new QPushButton(editLogWnd); + pushButton->setObjectName("pushButton"); + pushButton->setGeometry(QRect(30, 50, 101, 23)); + SectorView = new QTableWidget(editLogWnd); + SectorView->setObjectName("SectorView"); + SectorView->setGeometry(QRect(180, 10, 151, 131)); + pushButton_2 = new QPushButton(editLogWnd); + pushButton_2->setObjectName("pushButton_2"); + pushButton_2->setGeometry(QRect(110, 330, 75, 23)); + LoadDump = new QPushButton(editLogWnd); + LoadDump->setObjectName("LoadDump"); + LoadDump->setGeometry(QRect(20, 330, 75, 23)); + label = new QLabel(editLogWnd); + label->setObjectName("label"); + label->setGeometry(QRect(520, 10, 47, 13)); + textBrowser_2 = new QTextBrowser(editLogWnd); + textBrowser_2->setObjectName("textBrowser_2"); + textBrowser_2->setGeometry(QRect(570, 10, 256, 361)); + textBrowser_2->setAcceptRichText(true); + SampleView = new QTableWidget(editLogWnd); + SampleView->setObjectName("SampleView"); + SampleView->setGeometry(QRect(180, 180, 171, 41)); + ResetSampleAddr = new QPushButton(editLogWnd); + ResetSampleAddr->setObjectName("ResetSampleAddr"); + ResetSampleAddr->setGeometry(QRect(340, 10, 101, 23)); + + retranslateUi(editLogWnd); + QObject::connect(okB, &QPushButton::clicked, editLogWnd, qOverload<>(&QDialog::accept)); + + okB->setDefault(true); + + + QMetaObject::connectSlotsByName(editLogWnd); + } // setupUi + + void retranslateUi(QDialog *editLogWnd) + { + editLogWnd->setWindowTitle(QCoreApplication::translate("editLogWnd", "OSTC Companion settings", nullptr)); + WriteAllHeader->setText(QCoreApplication::translate("editLogWnd", "WriteAllHeader", nullptr)); + okB->setText(QCoreApplication::translate("editLogWnd", "OK", nullptr)); +#if QT_CONFIG(shortcut) + okB->setShortcut(QCoreApplication::translate("editLogWnd", "Return", nullptr)); +#endif // QT_CONFIG(shortcut) + ReadAllSamples->setText(QCoreApplication::translate("editLogWnd", "ReadAllSamples", nullptr)); + WriteAllSamples->setText(QCoreApplication::translate("editLogWnd", "WriteAllSamples", nullptr)); + pushButton->setText(QCoreApplication::translate("editLogWnd", "ReadAllHeader", nullptr)); + pushButton_2->setText(QCoreApplication::translate("editLogWnd", "Save Dump", nullptr)); + LoadDump->setText(QCoreApplication::translate("editLogWnd", "Load Dump", nullptr)); + label->setText(QCoreApplication::translate("editLogWnd", "Info", nullptr)); + ResetSampleAddr->setText(QCoreApplication::translate("editLogWnd", "ResetSampleAddr", nullptr)); + } // retranslateUi + +}; + +namespace Ui { + class editLogWnd: public Ui_editLogWnd {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // UI_LOGEDITOR_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui_MainWindow.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,300 @@ +/******************************************************************************** +** Form generated from reading UI file 'MainWindow.ui' +** +** Created by: Qt User Interface Compiler version 6.10.1 +** +** WARNING! All changes made in this file will be lost when recompiling UI file! +********************************************************************************/ + +#ifndef UI_MAINWINDOW_H +#define UI_MAINWINDOW_H + +#include <QtCore/QVariant> +#include <QtGui/QIcon> +#include <QtWidgets/QApplication> +#include <QtWidgets/QComboBox> +#include <QtWidgets/QGridLayout> +#include <QtWidgets/QHBoxLayout> +#include <QtWidgets/QLabel> +#include <QtWidgets/QMainWindow> +#include <QtWidgets/QPlainTextEdit> +#include <QtWidgets/QProgressBar> +#include <QtWidgets/QPushButton> +#include <QtWidgets/QSpacerItem> +#include <QtWidgets/QToolButton> +#include <QtWidgets/QVBoxLayout> +#include <QtWidgets/QWidget> + +QT_BEGIN_NAMESPACE + +class Ui_MainWindow +{ +public: + QWidget *centralWidget; + QGridLayout *gridLayout; + QProgressBar *progressBar; + QComboBox *computerType; + QVBoxLayout *verticalLayout; + QHBoxLayout *horizontalLayout; + QPushButton *connectButton; + QPushButton *dateButton; + QPushButton *nameButton; + QPushButton *iconButton; + QPushButton *signalButton; + QPushButton *upgradeButton; + QPushButton *closeButton; + QSpacerItem *verticalSpacer; + QPushButton *quitButton; + QLabel *companionUrlL; + QSpacerItem *horizontalSpacer_2; + QToolButton *settingsButton; + QPlainTextEdit *console; + QLabel *computerImage; + QSpacerItem *horizontalSpacer_3; + QSpacerItem *horizontalSpacer; + QSpacerItem *verticalSpacer_3; + QSpacerItem *verticalSpacer_2; + QPushButton *editLog; + + void setupUi(QMainWindow *MainWindow) + { + if (MainWindow->objectName().isEmpty()) + MainWindow->setObjectName("MainWindow"); + MainWindow->resize(451, 418); + MainWindow->setWindowTitle(QString::fromUtf8("OSTC Companion")); + QIcon icon; + icon.addFile(QString::fromUtf8(":/Images/app_OSTC_Companion.svg"), QSize(), QIcon::Mode::Normal, QIcon::State::Off); + MainWindow->setWindowIcon(icon); + centralWidget = new QWidget(MainWindow); + centralWidget->setObjectName("centralWidget"); + gridLayout = new QGridLayout(centralWidget); + gridLayout->setSpacing(6); + gridLayout->setContentsMargins(11, 11, 11, 11); + gridLayout->setObjectName("gridLayout"); + progressBar = new QProgressBar(centralWidget); + progressBar->setObjectName("progressBar"); + progressBar->setAlignment(Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter); + progressBar->setTextVisible(true); + + gridLayout->addWidget(progressBar, 7, 0, 1, 4); + + computerType = new QComboBox(centralWidget); + computerType->addItem(QString::fromUtf8("OSTC2 mk.2/2n/2c")); + computerType->addItem(QString::fromUtf8("OSTC hwOS (USB)")); + computerType->addItem(QString::fromUtf8("OSTC hwOS (Bluetooth)")); + computerType->addItem(QString::fromUtf8("OSTC4")); + computerType->setObjectName("computerType"); + + gridLayout->addWidget(computerType, 0, 1, 1, 1); + + verticalLayout = new QVBoxLayout(); + verticalLayout->setSpacing(0); + verticalLayout->setObjectName("verticalLayout"); + verticalLayout->setContentsMargins(-1, -1, 0, 0); + horizontalLayout = new QHBoxLayout(); + horizontalLayout->setSpacing(6); + horizontalLayout->setObjectName("horizontalLayout"); + connectButton = new QPushButton(centralWidget); + connectButton->setObjectName("connectButton"); + connectButton->setMinimumSize(QSize(0, 23)); + connectButton->setStyleSheet(QString::fromUtf8("QPushButton {\n" +" border: 1px solid blue;\n" +" border-radius: 6px;\n" +" background: qlineargradient(x1:0, y1:0, x2:0, y2:1, \n" +" stop:0.00 white,\n" +" stop:0.55 #55aaff,\n" +" stop:0.56 #4964ff,\n" +" stop:1.00 #55aaff)\n" +"}")); + connectButton->setAutoDefault(true); + + horizontalLayout->addWidget(connectButton); + + + verticalLayout->addLayout(horizontalLayout); + + dateButton = new QPushButton(centralWidget); + dateButton->setObjectName("dateButton"); + + verticalLayout->addWidget(dateButton); + + nameButton = new QPushButton(centralWidget); + nameButton->setObjectName("nameButton"); + + verticalLayout->addWidget(nameButton); + + iconButton = new QPushButton(centralWidget); + iconButton->setObjectName("iconButton"); + + verticalLayout->addWidget(iconButton); + + signalButton = new QPushButton(centralWidget); + signalButton->setObjectName("signalButton"); + + verticalLayout->addWidget(signalButton); + + upgradeButton = new QPushButton(centralWidget); + upgradeButton->setObjectName("upgradeButton"); + + verticalLayout->addWidget(upgradeButton); + + closeButton = new QPushButton(centralWidget); + closeButton->setObjectName("closeButton"); + + verticalLayout->addWidget(closeButton); + + verticalSpacer = new QSpacerItem(105, 8, QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Minimum); + + verticalLayout->addItem(verticalSpacer); + + quitButton = new QPushButton(centralWidget); + quitButton->setObjectName("quitButton"); + + verticalLayout->addWidget(quitButton); + + + gridLayout->addLayout(verticalLayout, 0, 3, 6, 1); + + companionUrlL = new QLabel(centralWidget); + companionUrlL->setObjectName("companionUrlL"); + companionUrlL->setText(QString::fromUtf8("<html><head/><body><p>See us on <a href=\"https://ostc-planner.net/wp/companion/\"><span style=\" text-decoration: underline; color:#0000ff;\">ostc-planner.net/wp/companion</span></a></p></body></html>")); + companionUrlL->setOpenExternalLinks(true); + + gridLayout->addWidget(companionUrlL, 6, 0, 1, 4); + + horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Minimum); + + gridLayout->addItem(horizontalSpacer_2, 2, 0, 1, 1); + + settingsButton = new QToolButton(centralWidget); + settingsButton->setObjectName("settingsButton"); + QIcon icon1; + icon1.addFile(QString::fromUtf8(":/Images/Settings.svg"), QSize(), QIcon::Mode::Normal, QIcon::State::Off); + settingsButton->setIcon(icon1); + + gridLayout->addWidget(settingsButton, 0, 0, 1, 1); + + console = new QPlainTextEdit(centralWidget); + console->setObjectName("console"); + console->setMinimumSize(QSize(0, 46)); + console->setFrameShadow(QFrame::Shadow::Sunken); + console->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); + console->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); + console->setUndoRedoEnabled(false); + console->setReadOnly(true); + console->setCenterOnScroll(false); + console->setProperty("tabStopWidth", QVariant(4)); + + gridLayout->addWidget(console, 8, 0, 1, 4); + + computerImage = new QLabel(centralWidget); + computerImage->setObjectName("computerImage"); + QSizePolicy sizePolicy(QSizePolicy::Policy::Fixed, QSizePolicy::Policy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(computerImage->sizePolicy().hasHeightForWidth()); + computerImage->setSizePolicy(sizePolicy); + computerImage->setMinimumSize(QSize(160, 120)); + computerImage->setMaximumSize(QSize(160, 120)); + computerImage->setPixmap(QPixmap(QString::fromUtf8(":/Images/frog_160x120.png"))); + + gridLayout->addWidget(computerImage, 2, 1, 1, 1); + + horizontalSpacer_3 = new QSpacerItem(40, 20, QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Minimum); + + gridLayout->addItem(horizontalSpacer_3, 2, 2, 1, 1); + + horizontalSpacer = new QSpacerItem(8, 20, QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Minimum); + + gridLayout->addItem(horizontalSpacer, 0, 2, 1, 1); + + verticalSpacer_3 = new QSpacerItem(20, 0, QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Minimum); + + gridLayout->addItem(verticalSpacer_3, 5, 1, 1, 1); + + verticalSpacer_2 = new QSpacerItem(20, 0, QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Minimum); + + gridLayout->addItem(verticalSpacer_2, 1, 1, 1, 1); + + editLog = new QPushButton(centralWidget); + editLog->setObjectName("editLog"); + editLog->setEnabled(false); + + gridLayout->addWidget(editLog, 5, 2, 1, 1); + + MainWindow->setCentralWidget(centralWidget); + + retranslateUi(MainWindow); + QObject::connect(computerType, SIGNAL(activated(int)), MainWindow, SLOT(changeTypeSlot())); + QObject::connect(quitButton, &QPushButton::clicked, MainWindow, qOverload<>(&QMainWindow::close)); + QObject::connect(dateButton, SIGNAL(clicked()), MainWindow, SLOT(dateSlot())); + QObject::connect(nameButton, SIGNAL(clicked()), MainWindow, SLOT(nameSlot())); + QObject::connect(iconButton, SIGNAL(clicked()), MainWindow, SLOT(iconSlot())); + QObject::connect(upgradeButton, SIGNAL(clicked()), MainWindow, SLOT(upgradeSlot())); + QObject::connect(connectButton, SIGNAL(clicked()), MainWindow, SLOT(connectSlot())); + QObject::connect(settingsButton, SIGNAL(clicked()), MainWindow, SLOT(settingsSlot())); + QObject::connect(closeButton, SIGNAL(clicked()), MainWindow, SLOT(closeSlot())); + + computerType->setCurrentIndex(0); + + + QMetaObject::connectSlotsByName(MainWindow); + } // setupUi + + void retranslateUi(QMainWindow *MainWindow) + { + +#if QT_CONFIG(tooltip) + computerType->setToolTip(QCoreApplication::translate("MainWindow", "H&W dive computer model.", nullptr)); +#endif // QT_CONFIG(tooltip) +#if QT_CONFIG(tooltip) + connectButton->setToolTip(QCoreApplication::translate("MainWindow", "Open or re-open the last selected USB or Bluetooth port (See Preferences).\n" +"NOTE: make sure to connect the serial cable, or to set the bluetooth mode first...", nullptr)); +#endif // QT_CONFIG(tooltip) + connectButton->setText(QCoreApplication::translate("MainWindow", "Connect", nullptr)); +#if QT_CONFIG(tooltip) + dateButton->setToolTip(QCoreApplication::translate("MainWindow", "Set the H&W computer date and time.\n" +"Can be automatic at each connect, if asked to in the Preferences.\n" +"(Valid once device is connected).", nullptr)); +#endif // QT_CONFIG(tooltip) + dateButton->setText(QCoreApplication::translate("MainWindow", "Set Date && Time", nullptr)); +#if QT_CONFIG(tooltip) + nameButton->setToolTip(QCoreApplication::translate("MainWindow", "Change the name displayed on the H&W device.\n" +"(Valid once device is connected).", nullptr)); +#endif // QT_CONFIG(tooltip) + nameButton->setText(QCoreApplication::translate("MainWindow", "Set Name...", nullptr)); +#if QT_CONFIG(tooltip) + iconButton->setToolTip(QCoreApplication::translate("MainWindow", "Upload a customization icon.\n" +"Only supported on Frog yet.\n" +"(Valid once device is connected).", nullptr)); +#endif // QT_CONFIG(tooltip) + iconButton->setText(QCoreApplication::translate("MainWindow", "Set Icon...", nullptr)); + signalButton->setText(QCoreApplication::translate("MainWindow", "Check Signal", nullptr)); +#if QT_CONFIG(tooltip) + upgradeButton->setToolTip(QCoreApplication::translate("MainWindow", "Ask for a firmware file, and upload it to the H&W device.\n" +"Can be done before or after device (re-)connection.", nullptr)); +#endif // QT_CONFIG(tooltip) + upgradeButton->setText(QCoreApplication::translate("MainWindow", "Upgrade Firmware...", nullptr)); +#if QT_CONFIG(tooltip) + closeButton->setToolTip(QCoreApplication::translate("MainWindow", "Close USB or Bluetooth connection to the device.", nullptr)); +#endif // QT_CONFIG(tooltip) + closeButton->setText(QCoreApplication::translate("MainWindow", "Close", nullptr)); + quitButton->setText(QCoreApplication::translate("MainWindow", "Quit", nullptr)); +#if QT_CONFIG(tooltip) + settingsButton->setToolTip(QCoreApplication::translate("MainWindow", "Open the Preferences menu.", nullptr)); +#endif // QT_CONFIG(tooltip) + settingsButton->setText(QCoreApplication::translate("MainWindow", "...", nullptr)); + computerImage->setText(QString()); + editLog->setText(QCoreApplication::translate("MainWindow", "Edit Log", nullptr)); + (void)MainWindow; + } // retranslateUi + +}; + +namespace Ui { + class MainWindow: public Ui_MainWindow {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // UI_MAINWINDOW_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui_Settings.h Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,252 @@ +/******************************************************************************** +** Form generated from reading UI file 'Settings.ui' +** +** Created by: Qt User Interface Compiler version 5.13.0 +** +** WARNING! All changes made in this file will be lost when recompiling UI file! +********************************************************************************/ + +#ifndef UI_SETTINGS_H +#define UI_SETTINGS_H + +#include <QtCore/QVariant> +#include <QtWidgets/QApplication> +#include <QtWidgets/QCheckBox> +#include <QtWidgets/QComboBox> +#include <QtWidgets/QDialog> +#include <QtWidgets/QFrame> +#include <QtWidgets/QGridLayout> +#include <QtWidgets/QHBoxLayout> +#include <QtWidgets/QLabel> +#include <QtWidgets/QPushButton> +#include <QtWidgets/QSpacerItem> +#include <QtWidgets/QToolButton> + +QT_BEGIN_NAMESPACE + +class Ui_Settings +{ +public: + QGridLayout *gridLayout; + QFrame *line_3; + QCheckBox *forceFirmwareUpdate; + QComboBox *portMenu; + QCheckBox *autoSetDateTimeCB; + QCheckBox *forceFontlibUpdate; + QLabel *noPortLabel; + QComboBox *languageMenu; + QToolButton *updatePorts; + QHBoxLayout *horizontalLayout; + QPushButton *resetB; + QSpacerItem *horizontalSpacer_2; + QPushButton *okB; + QPushButton *cancelB; + QLabel *label; + QCheckBox *forceRTEUpdate; + QFrame *line_4; + QLabel *label_2; + QSpacerItem *horizontalSpacer; + QFrame *line; + QCheckBox *useFastMode; + + void setupUi(QDialog *Settings) + { + if (Settings->objectName().isEmpty()) + Settings->setObjectName(QString::fromUtf8("Settings")); + Settings->resize(341, 293); + gridLayout = new QGridLayout(Settings); + gridLayout->setObjectName(QString::fromUtf8("gridLayout")); + line_3 = new QFrame(Settings); + line_3->setObjectName(QString::fromUtf8("line_3")); + line_3->setFrameShape(QFrame::HLine); + line_3->setFrameShadow(QFrame::Sunken); + + gridLayout->addWidget(line_3, 10, 0, 1, 3); + + forceFirmwareUpdate = new QCheckBox(Settings); + forceFirmwareUpdate->setObjectName(QString::fromUtf8("forceFirmwareUpdate")); + + gridLayout->addWidget(forceFirmwareUpdate, 6, 0, 1, 2); + + portMenu = new QComboBox(Settings); + portMenu->setObjectName(QString::fromUtf8("portMenu")); + portMenu->setEditable(true); + + gridLayout->addWidget(portMenu, 2, 1, 1, 1); + + autoSetDateTimeCB = new QCheckBox(Settings); + autoSetDateTimeCB->setObjectName(QString::fromUtf8("autoSetDateTimeCB")); + + gridLayout->addWidget(autoSetDateTimeCB, 11, 0, 1, 3); + + forceFontlibUpdate = new QCheckBox(Settings); + forceFontlibUpdate->setObjectName(QString::fromUtf8("forceFontlibUpdate")); + + gridLayout->addWidget(forceFontlibUpdate, 8, 0, 1, 2); + + noPortLabel = new QLabel(Settings); + noPortLabel->setObjectName(QString::fromUtf8("noPortLabel")); + QSizePolicy sizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(noPortLabel->sizePolicy().hasHeightForWidth()); + noPortLabel->setSizePolicy(sizePolicy); + noPortLabel->setTextFormat(Qt::RichText); + noPortLabel->setAlignment(Qt::AlignCenter); + noPortLabel->setOpenExternalLinks(true); + + gridLayout->addWidget(noPortLabel, 4, 0, 1, 3); + + languageMenu = new QComboBox(Settings); + QIcon icon; + icon.addFile(QString::fromUtf8(":/icons/Icons/german.png"), QSize(), QIcon::Normal, QIcon::Off); + languageMenu->addItem(icon, QString::fromUtf8("Deutsch")); + QIcon icon1; + icon1.addFile(QString::fromUtf8(":/icons/Icons/english.png"), QSize(), QIcon::Normal, QIcon::Off); + languageMenu->addItem(icon1, QString::fromUtf8("English")); + QIcon icon2; + icon2.addFile(QString::fromUtf8(":/icons/Icons/spanish.png"), QSize(), QIcon::Normal, QIcon::Off); + languageMenu->addItem(icon2, QString::fromUtf8("Espa\303\261ol")); + QIcon icon3; + icon3.addFile(QString::fromUtf8(":/icons/Icons/french.png"), QSize(), QIcon::Normal, QIcon::Off); + languageMenu->addItem(icon3, QString::fromUtf8("Fran\303\247ais")); + QIcon icon4; + icon4.addFile(QString::fromUtf8(":/icons/Icons/italian.png"), QSize(), QIcon::Normal, QIcon::Off); + languageMenu->addItem(icon4, QString::fromUtf8("Italiano")); + QIcon icon5; + icon5.addFile(QString::fromUtf8(":/icons/Icons/russian.png"), QSize(), QIcon::Normal, QIcon::Off); + languageMenu->addItem(icon5, QString::fromUtf8("\320\240\321\203\321\201\321\201\320\272\320\270\320\271")); + languageMenu->setObjectName(QString::fromUtf8("languageMenu")); + languageMenu->setInsertPolicy(QComboBox::NoInsert); + + gridLayout->addWidget(languageMenu, 0, 1, 1, 1); + + updatePorts = new QToolButton(Settings); + updatePorts->setObjectName(QString::fromUtf8("updatePorts")); + updatePorts->setText(QString::fromUtf8("...")); + + gridLayout->addWidget(updatePorts, 2, 2, 1, 1); + + horizontalLayout = new QHBoxLayout(); + horizontalLayout->setSpacing(0); + horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout")); + resetB = new QPushButton(Settings); + resetB->setObjectName(QString::fromUtf8("resetB")); + + horizontalLayout->addWidget(resetB); + + horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + horizontalLayout->addItem(horizontalSpacer_2); + + okB = new QPushButton(Settings); + okB->setObjectName(QString::fromUtf8("okB")); + okB->setAutoDefault(false); + + horizontalLayout->addWidget(okB); + + cancelB = new QPushButton(Settings); + cancelB->setObjectName(QString::fromUtf8("cancelB")); + + horizontalLayout->addWidget(cancelB); + + + gridLayout->addLayout(horizontalLayout, 15, 0, 1, 3); + + label = new QLabel(Settings); + label->setObjectName(QString::fromUtf8("label")); + label->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout->addWidget(label, 0, 0, 1, 1); + + forceRTEUpdate = new QCheckBox(Settings); + forceRTEUpdate->setObjectName(QString::fromUtf8("forceRTEUpdate")); + + gridLayout->addWidget(forceRTEUpdate, 7, 0, 1, 2); + + line_4 = new QFrame(Settings); + line_4->setObjectName(QString::fromUtf8("line_4")); + line_4->setFrameShape(QFrame::HLine); + line_4->setFrameShadow(QFrame::Sunken); + + gridLayout->addWidget(line_4, 5, 0, 1, 3); + + label_2 = new QLabel(Settings); + label_2->setObjectName(QString::fromUtf8("label_2")); + QSizePolicy sizePolicy1(QSizePolicy::Expanding, QSizePolicy::Preferred); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(label_2->sizePolicy().hasHeightForWidth()); + label_2->setSizePolicy(sizePolicy1); + label_2->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout->addWidget(label_2, 2, 0, 1, 1); + + horizontalSpacer = new QSpacerItem(0, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + gridLayout->addItem(horizontalSpacer, 0, 2, 1, 1); + + line = new QFrame(Settings); + line->setObjectName(QString::fromUtf8("line")); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + + gridLayout->addWidget(line, 14, 0, 1, 3); + + useFastMode = new QCheckBox(Settings); + useFastMode->setObjectName(QString::fromUtf8("useFastMode")); + + gridLayout->addWidget(useFastMode, 13, 0, 1, 3); + + + retranslateUi(Settings); + QObject::connect(okB, SIGNAL(clicked()), Settings, SLOT(accept())); + QObject::connect(cancelB, SIGNAL(clicked()), Settings, SLOT(reject())); + QObject::connect(languageMenu, SIGNAL(activated(int)), Settings, SLOT(languageSlot(int))); + QObject::connect(updatePorts, SIGNAL(clicked()), Settings, SLOT(updatePortsSlot())); + QObject::connect(resetB, SIGNAL(clicked()), Settings, SLOT(resetSettingsSlot())); + + languageMenu->setCurrentIndex(0); + okB->setDefault(true); + + + QMetaObject::connectSlotsByName(Settings); + } // setupUi + + void retranslateUi(QDialog *Settings) + { + Settings->setWindowTitle(QCoreApplication::translate("Settings", "OSTC Companion settings", nullptr)); + forceFirmwareUpdate->setText(QCoreApplication::translate("Settings", "Force Firmware update", nullptr)); +#if QT_CONFIG(tooltip) + autoSetDateTimeCB->setToolTip(QCoreApplication::translate("Settings", "If checked, any configuration upload will also setup date and time.", nullptr)); +#endif // QT_CONFIG(tooltip) + autoSetDateTimeCB->setText(QCoreApplication::translate("Settings", "Auto setup of date && time", nullptr)); + forceFontlibUpdate->setText(QCoreApplication::translate("Settings", "Force FontLib update", nullptr)); + +#if QT_CONFIG(tooltip) + updatePorts->setToolTip(QCoreApplication::translate("Settings", "Update port list with currently connected USB or Bluetooth devices.", nullptr)); +#endif // QT_CONFIG(tooltip) + resetB->setText(QCoreApplication::translate("Settings", "Restore Defaults", nullptr)); + okB->setText(QCoreApplication::translate("Settings", "OK", nullptr)); +#if QT_CONFIG(shortcut) + okB->setShortcut(QCoreApplication::translate("Settings", "Return", nullptr)); +#endif // QT_CONFIG(shortcut) + cancelB->setText(QCoreApplication::translate("Settings", "Cancel", nullptr)); +#if QT_CONFIG(shortcut) + cancelB->setShortcut(QCoreApplication::translate("Settings", "Ctrl+W", nullptr)); +#endif // QT_CONFIG(shortcut) + label->setText(QCoreApplication::translate("Settings", "Language:", nullptr)); + forceRTEUpdate->setText(QCoreApplication::translate("Settings", "Force RTE update", nullptr)); + label_2->setText(QCoreApplication::translate("Settings", "Communication port:", nullptr)); + useFastMode->setText(QCoreApplication::translate("Settings", "Enable fast mode if supported", nullptr)); + } // retranslateUi + +}; + +namespace Ui { + class Settings: public Ui_Settings {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // UI_SETTINGS_H
