From 4667feb2404466677115e4eecdea8070bce68863 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Fri, 3 Jan 2025 12:31:13 +0100 Subject: [PATCH] UI code reformatting, add PN532 NFC driver --- CMakeLists.txt | 1 + src/common/defs.hpp | 39 ++-- src/common/exthw/lcd.cpp | 6 +- src/common/exthw/nfc.cpp | 411 +++++++++++++++++++++++++++++++++++++++ src/common/exthw/nfc.hpp | 228 ++++++++++++++++++++++ src/main/uibase.cpp | 87 +++++++-- src/main/uicommon.cpp | 145 +++++++++++--- src/main/uimodals.cpp | 129 +++++++++--- src/main/uimodals.hpp | 11 +- 9 files changed, 957 insertions(+), 100 deletions(-) create mode 100644 src/common/exthw/nfc.cpp create mode 100644 src/common/exthw/nfc.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9335638..65cf595 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ set( set( exthwSources src/common/exthw/lcd.cpp + src/common/exthw/nfc.cpp ) set( fsSources diff --git a/src/common/defs.hpp b/src/common/defs.hpp index 8812edd..43f146f 100644 --- a/src/common/defs.hpp +++ b/src/common/defs.hpp @@ -1,5 +1,5 @@ /* - * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * 573in1 - Copyright (C) 2022-2025 spicyjpeg * * 573in1 is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software @@ -40,18 +40,25 @@ #define CH_RIGHT_ARROW_ALT "\u2192" #define CH_INVALID_CHAR "\ufffd" -#define CH_CIRCLE_BUTTON "\u25cb" -#define CH_X_BUTTON "\u2715" -#define CH_TRIANGLE_BUTTON "\u25b3" -#define CH_SQUARE_BUTTON "\u25a1" -#define CH_LEFT_BUTTON "\u25c1" -#define CH_RIGHT_BUTTON "\u25b7" -#define CH_START_BUTTON "\u25ad" -#define CH_CLOSED_LOCK "\U0001f512" -#define CH_OPEN_LOCK "\U0001f513" -#define CH_CDROM_ICON "\U0001f4bf" -#define CH_HDD_ICON "\U0001f4bd" -#define CH_HOST_ICON "\U0001f50c" -#define CH_DIR_ICON "\U0001f4c1" -#define CH_PARENT_DIR_ICON "\U0001f4c2" -#define CH_FILE_ICON "\U0001f4c4" +#define CH_PS1_CIRCLE_BUTTON "\u25cb" +#define CH_PS1_X_BUTTON "\u2715" +#define CH_PS1_TRIANGLE_BUTTON "\u25b3" +#define CH_PS1_SQUARE_BUTTON "\u25a1" +#define CH_PS1_SELECT_BUTTON "\u25ac" +#define CH_PS1_START_BUTTON "\u25ba" + +#define CH_LEFT_BUTTON "\u25c1" +#define CH_RIGHT_BUTTON "\u25b7" +#define CH_START_BUTTON "\u25ad" + +#define CH_CLOSED_LOCK "\U0001f512" +#define CH_OPEN_LOCK "\U0001f513" + +#define CH_CDROM_ICON "\U0001f4bf" +#define CH_HDD_ICON "\U0001f4bd" +#define CH_MEMORY_CARD_ICON "\U0001f4be" +#define CH_POCKETSTATION_ICON "\U0001f4f1" +#define CH_HOST_ICON "\U0001f50c" +#define CH_DIR_ICON "\U0001f4c1" +#define CH_PARENT_DIR_ICON "\U0001f4c2" +#define CH_FILE_ICON "\U0001f4c4" diff --git a/src/common/exthw/lcd.cpp b/src/common/exthw/lcd.cpp index 1ccfa90..56efde2 100644 --- a/src/common/exthw/lcd.cpp +++ b/src/common/exthw/lcd.cpp @@ -24,8 +24,8 @@ #include "ps1/system.h" /* - * This is a simple driver for a character LCD module, wired to the EXT-OUT - * connector on the 573 main board as follows: + * This is a simple driver for a standard character LCD module, wired to the + * EXT-OUT (CN4) header on the 573 main board as follows: * * | EXT-OUT pin | LCD pin | * | ----------: | :----------- | @@ -40,6 +40,8 @@ * * The `V0` (bias voltage) pin shall be connected to ground through an * appropriate resistor or potentiometer in order to set the display's contrast. + * The backlight is not controlled by this driver and can be left unconnected or + * hardwired to power. */ namespace exthw { diff --git a/src/common/exthw/nfc.cpp b/src/common/exthw/nfc.cpp new file mode 100644 index 0000000..8d4fff1 --- /dev/null +++ b/src/common/exthw/nfc.cpp @@ -0,0 +1,411 @@ +/* + * 573in1 - Copyright (C) 2022-2025 spicyjpeg + * + * 573in1 is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * 573in1. If not, see . + */ + +#include +#include +#include "common/exthw/nfc.hpp" +#include "common/util/log.hpp" +#include "common/util/templates.hpp" +#include "common/io.hpp" +#include "ps1/system.h" + +/* + * This is a simple driver for a PN532 NFC reader module connected to the 573's + * serial port. Currently the only supported feature is reading the ID of a + * Mifare or FeliCa card. The module can be wired to the cartridge slot, or to + * the unpopulated CN24 header on main board revisions that have it, as follows: + * + * | CN24 pin | Cart slot pin | Module pin | + * | -------: |- ------------: | :------------------------------------------- | + * | | 21, 22, 41, 42 | `VCC` (via 3.3V regulator, see note) | + * | 1 | 5 | `SCL`/`HSU_RX` (via level shifter, see note) | + * | 2 | 6 | `SDA`/`HSU_TX` (via level shifter, see note) | + * | 3, 4 | 1, 2, 8, 9 | `GND`, `I0`, `I1` | + * | 5, 6 | 43, 44 | None (short pins together on 573 side) | + * + * The PN532 operates at 3.3V so a voltage regulator and level shifter are + * required to adapt it to the 573's 5V signals (some modules already include + * them and can thus be wired directly). The module must be configured for HSU + * (serial) mode through the appropriate jumpers or DIP switches, or by + * grounding the `I0` and `I1` pins. + * + * Alternatively the PN532 module may be connected through an RS-232 level + * translator to the "network" port on the security cartridge (if any): + * + * | "Network" pin | Module pin | + * | ------------: | :-------------------------------------- | + * | 1 | `SCL`/`HSU_RX` (via RS-232 transceiver) | + * | 2 | `SDA`/`HSU_TX` (via RS-232 transceiver) | + * | 5 | `GND`, `I0`, `I1` | + * + * The module and transceiver will have to be powered from an external source as + * the "network" port is galvanically isolated from the rest of the system. + */ + +namespace exthw { + +/* PN532 packet structures */ + +void PN532PacketHeader::updateChecksum(void) { +#if 0 + lengthChecksum = -int(length) & 0xff; +#endif + + auto data = &address; + data[length] = -int(util::sum(data, length)) & 0xff; +} + +bool PN532PacketHeader::validateChecksum(void) const { + if (lengthChecksum != (-int(length) & 0xff)) + return false; + + auto data = &address; + uint8_t value = -int(util::sum(data, length)) & 0xff; + + if (value != data[length]) { + LOG_IO("mismatch, exp=0x%02x, got=0x%02x", value, data[length]); + return false; + } + + return true; +} + +void PN532PacketHeader::encodeCommand(int paramLength) { + paramLength += 2; + + preamble = PN532_PACKET_PREAMBLE; + startCode[0] = PN532_PACKET_START1; + startCode[1] = PN532_PACKET_START2; + length = paramLength & 0xff; + lengthChecksum = -paramLength & 0xff; + address = PN532_ADDR_DEVICE; + + updateChecksum(); +} + +bool PN532PacketHeader::decodeResponse(void) const { + if ( + (startCode[0] != PN532_PACKET_START1) || + (startCode[1] != PN532_PACKET_START2) + ) + return false; + if (address != PN532_ADDR_HOST) + return false; + + return validateChecksum(); +} + +bool PN532PacketHeader::isErrorResponse(void) const { + if ( + (startCode[0] != PN532_PACKET_START1) || + (startCode[1] != PN532_PACKET_START2) + ) + return false; + if ((length != 1) || (lengthChecksum != 0xff)) + return false; + + return (address == PN532_ADDR_ERROR); +} + +void PN532ExtPacketHeader::updateChecksum(void) { +#if 0 + lengthChecksum = -int(length[0] + length[1]) & 0xff; +#endif + + auto data = &address; + auto length = getDataLength(); + data[length] = -int(util::sum(data, length)) & 0xff; +} + +bool PN532ExtPacketHeader::validateChecksum(void) const { + if (lengthChecksum != (-int(length[0] + length[1]) & 0xff)) + return false; + + auto data = &address; + auto _length = getDataLength(); + uint8_t value = -int(util::sum(data, _length)) & 0xff; + + if (value != data[_length]) { + LOG_IO("mismatch, exp=0x%02x, got=0x%02x", value, data[_length]); + return false; + } + + return true; +} + +void PN532ExtPacketHeader::encodeCommand(int paramLength) { + paramLength += 2; + + preamble = PN532_PACKET_PREAMBLE; + startCode[0] = PN532_PACKET_START1; + startCode[1] = PN532_PACKET_START2; + packetMagic[0] = 0xff; + packetMagic[1] = 0xff; + length[0] = (paramLength >> 8) & 0xff; + length[1] = (paramLength >> 0) & 0xff; + lengthChecksum = -int(length[0] + length[1]) & 0xff; + address = PN532_ADDR_DEVICE; + + updateChecksum(); +} + +bool PN532ExtPacketHeader::decodeResponse(void) const { + if ( + (startCode[0] != PN532_PACKET_START1) || + (startCode[1] != PN532_PACKET_START2) + ) + return false; + if ((packetMagic[0] != 0xff) || (packetMagic[1] != 0xff)) + return false; + if (address != PN532_ADDR_HOST) + return false; + + return validateChecksum(); +} + +void PN532ACKPacket::encodeACK(bool isACK) { + preamble = PN532_PACKET_PREAMBLE; + startCode[0] = PN532_PACKET_START1; + startCode[1] = PN532_PACKET_START2; + packetMagic[0] = isACK ? 0x00 : 0xff; + packetMagic[1] = isACK ? 0xff : 0x00; + postamble = PN532_PACKET_PREAMBLE; +} + +bool PN532ACKPacket::decodeACK(void) const { + if ( + (startCode[0] != PN532_PACKET_START1) || + (startCode[1] != PN532_PACKET_START2) + ) + return false; + if ((packetMagic[0] != 0x00) || (packetMagic[1] != 0xff)) + return false; + + return true; +} + +/* PN532 driver */ + +static constexpr int _POWER_DOWN_DELAY = 1000; +static constexpr int _WAKEUP_DELAY = 1000; +static constexpr int _ACK_TIMEOUT = 1000; +static constexpr int _RESPONSE_TIMEOUT = 3000000; + +static constexpr int _DEFAULT_BAUD_RATE = 115200; +static constexpr int _MAX_SEND_ATTEMPTS = 3; +static constexpr int _MAX_RECEIVE_ATTEMPTS = 3; + +bool PN532Driver::_transact( + const PN532PacketHeader &request, + PN532PacketHeader &response, + size_t maxRespLength +) { + if (!_serial.isConnected()) { + LOG_IO("serial port not connected"); + return false; + } + if (_isIdle) { + // If the PN532 is powered down, it must be woken up by sending at least + // 5 rising edges on TX before it can accept a new command. + _serial.writeByte(PN532_PACKET_PREAMBLE); + delayMicroseconds(_WAKEUP_DELAY); + + _isIdle = false; + } + + // Keep sending the request until an acknowledge packet is received. + for (int i = _MAX_SEND_ATTEMPTS; i; i--) { + _serial.writeBytes(&request.preamble, request.getPacketLength()); + + PN532ACKPacket ack; + + if ( + _serial.readBytes(&ack.preamble, sizeof(ack), _ACK_TIMEOUT) + < sizeof(ack) + ) + continue; + if (!ack.decodeACK()) + continue; + + // Wait for a response, then validate it and send a NACK to request + // retransmission if needed. + for (int i = _MAX_RECEIVE_ATTEMPTS; i; i--) { + if (_serial.readBytes( + &response.preamble, + maxRespLength, + _RESPONSE_TIMEOUT + ) >= sizeof(response)) { + if (response.decodeResponse()) + return true; + + if (response.isErrorResponse()) { + LOG_IO("PN532 error"); + return false; + } + } + + ack.encodeACK(false); + _serial.writeBytes(&ack.preamble, sizeof(ack)); + } + + LOG_IO("too many receive attempts failed"); + return false; + } + + LOG_IO("too many send attempts failed"); + return false; +} + +bool PN532Driver::init(void) { + _serial.init(_DEFAULT_BAUD_RATE); + _isIdle = true; + + PN532Packet<4> packet; + + packet.command = PN532_GET_FIRMWARE_VERSION; + packet.encodeCommand(0); + + if (!_transact(packet, packet)) + return false; + + if (packet.param[0] != 0x32) { + LOG_IO("unsupported NFC chip, id=0x%02x", packet.param[0]); + return false; + } + + LOG_IO("found PN532 v%d.%d", packet.param[1], packet.param[2]); + + // This command is required to exit "low VBAT" mode. + packet.command = PN532_SAM_CONFIG; + packet.param[0] = PN532_SAM_MODE_NORMAL; + packet.encodeCommand(1); + + if (!_transact(packet, packet)) + return false; + + return setMaxRetries(0); +} + +bool PN532Driver::setMaxRetries(int count) { + if (count < 0) + count = 0xff; + + PN532Packet<4> packet; + + packet.command = PN532_RF_CONFIG; + packet.param[0] = PN532_RF_CONFIG_MAX_RTY_COM; + packet.param[1] = count; + packet.encodeCommand(2); + + if (!_transact(packet, packet)) + return false; + + packet.command = PN532_RF_CONFIG; + packet.param[0] = PN532_RF_CONFIG_MAX_RETRIES; + packet.param[1] = count; + packet.param[2] = count; + packet.param[3] = count; + packet.encodeCommand(4); + + return _transact(packet, packet); +} + +bool PN532Driver::goIdle(void) { + PN532Packet<1> packet; + + packet.command = PN532_POWER_DOWN; + packet.param[0] = PN532_WAKEUP_SOURCE_HSU; + packet.encodeCommand(1); + + if (!_transact(packet, packet)) + return false; + + delayMicroseconds(_POWER_DOWN_DELAY); + _isIdle = true; + return true; +} + +size_t PN532Driver::readISO14443CardID(uint8_t *output) { + PN532Packet<1 + 11> packet; + + packet.command = PN532_IN_LIST_TARGETS; + packet.param[0] = 1; + packet.param[1] = PN532_LIST_TARGETS_ISO14443A; + packet.encodeCommand(2); + + if (!_transact(packet, packet)) + return 0; + if (!packet.param[0]) + return 0; + + auto atqa = util::concat2(packet.param[2], packet.param[1]); + auto saq = packet.param[3]; + auto idLength = packet.param[4]; + +#if 0 + char buffer[32]; + + util::hexToString(buffer, &packet.param[5], idLength, '-'); + LOG_IO("%s", buffer); + LOG_IO("atqa=0x%04x, saq=0x%02x", atqa, saq); +#endif + + __builtin_memcpy(output, &packet.param[5], idLength); + return idLength; +} + +size_t PN532Driver::readFeliCaCardID(uint8_t *output, uint16_t systemCode) { + PN532Packet<1 + 20> packet; + + packet.command = PN532_IN_LIST_TARGETS; + packet.param[0] = 1; + packet.param[1] = PN532_LIST_TARGETS_FELICA_212; + packet.param[2] = FELICA_POLL; + packet.param[3] = (systemCode >> 8) & 0xff; + packet.param[4] = (systemCode >> 0) & 0xff; + packet.param[5] = FELICA_REQ_CODE_NONE; + packet.param[6] = 0; + packet.encodeCommand(7); + + if (!_transact(packet, packet)) + return 0; + if (!packet.param[0]) + return 0; + + auto respLength = packet.param[1]; + auto respCode = packet.param[2]; + + if ((respLength != 18) && (respLength != 20)) { + LOG_IO("invalid response length: 0x%02x", respLength); + return 0; + } + if (respCode != 0x01) { + LOG_IO("invalid response code: 0x%02x", respCode); + return 0; + } + +#if 0 + char buffer[24]; + + util::hexToString(buffer, &packet.param[3], 8, '-'); + LOG_IO("%s", buffer); +#endif + + __builtin_memcpy(output, &packet.param[3], 8); + return 8; +} + +} diff --git a/src/common/exthw/nfc.hpp b/src/common/exthw/nfc.hpp new file mode 100644 index 0000000..95f61d0 --- /dev/null +++ b/src/common/exthw/nfc.hpp @@ -0,0 +1,228 @@ +/* + * 573in1 - Copyright (C) 2022-2025 spicyjpeg + * + * 573in1 is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * 573in1. If not, see . + */ + +#pragma once + +#include +#include +#include "common/util/templates.hpp" +#include "common/io.hpp" + +namespace exthw { + +/* PN532 command definitions */ + +enum PN532Command : uint8_t { + PN532_DIAGNOSE = 0x00, + PN532_GET_FIRMWARE_VERSION = 0x02, + PN532_GET_GENERAL_STATUS = 0x04, + PN532_READ_REG = 0x06, + PN532_WRITE_REG = 0x08, + PN532_READ_GPIO = 0x0c, + PN532_WRITE_GPIO = 0x0e, + PN532_SET_BAUD_RATE = 0x10, + PN532_SET_PARAMETERS = 0x12, + PN532_SAM_CONFIG = 0x14, + PN532_POWER_DOWN = 0x16, + PN532_RF_CONFIG = 0x32, + PN532_IN_DATA_EXCHANGE = 0x40, + PN532_IN_COMMUNICATE_THRU = 0x42, + PN532_IN_DESELECT = 0x44, + PN532_IN_JUMP_FOR_PSL = 0x46, + PN532_IN_LIST_TARGETS = 0x4a, + PN532_IN_PSL = 0x4e, + PN532_IN_ATR = 0x50, + PN532_IN_RELEASE = 0x52, + PN532_IN_SELECT = 0x54, + PN532_IN_JUMP_FOR_DEP = 0x56, + PN532_RF_REGULATION_TEST = 0x58, + PN532_IN_AUTO_POLL = 0x60, + PN532_TG_GET_DATA = 0x86, + PN532_TG_GET_COMMAND = 0x88, + PN532_TG_GET_TARGET_STATUS = 0x8a, + PN532_TG_INIT = 0x8c, + PN532_TG_SET_DATA = 0x8e, + PN532_TG_SEND_RESPONSE = 0x90, + PN532_TG_SET_GENERAL_BYTES = 0x92, + PN532_TG_SET_METADATA = 0x94 +}; + +/* PN532 parameter definitions */ + +enum PN532SAMMode : uint8_t { + PN532_SAM_MODE_NORMAL = 0x01, + PN532_SAM_MODE_VIRTUAL_CARD = 0x02, + PN532_SAM_MODE_WIRED_CARD = 0x03, + PN532_SAM_MODE_DUAL_CARD = 0x04 +}; + +enum PN532RFConfigItem : uint8_t { + PN532_RF_CONFIG_FIELD = 0x01, + PN532_RF_CONFIG_TIMEOUTS = 0x02, + PN532_RF_CONFIG_MAX_RTY_COM = 0x04, + PN532_RF_CONFIG_MAX_RETRIES = 0x05, + PN532_RF_CONFIG_CIU_ISO14443A = 0x0a, + PN532_RF_CONFIG_CIU_FELICA = 0x0b, + PN532_RF_CONFIG_CIU_ISO14443B = 0x0c, + PN532_RF_CONFIG_CIU_ISO14443_4 = 0x0d +}; + +enum PN532ListTargetsType : uint8_t { + PN532_LIST_TARGETS_ISO14443A = 0x00, + PN532_LIST_TARGETS_FELICA_212 = 0x01, + PN532_LIST_TARGETS_FELICA_414 = 0x02, + PN532_LIST_TARGETS_ISO14443B = 0x03 +}; + +enum PN532WakeupSource : uint8_t { + PN532_WAKEUP_SOURCE_INT0 = 1 << 0, + PN532_WAKEUP_SOURCE_INT1 = 1 << 1, + PN532_WAKEUP_SOURCE_RF = 1 << 3, + PN532_WAKEUP_SOURCE_HSU = 1 << 4, + PN532_WAKEUP_SOURCE_SPI = 1 << 5, + PN532_WAKEUP_SOURCE_GPIO = 1 << 6, + PN532_WAKEUP_SOURCE_I2C = 1 << 7 +}; + +/* FeliCa definitions */ + +enum FeliCaCommand : uint8_t { + FELICA_POLL = 0x00, + FELICA_REQUEST_SERVICE = 0x02, + FELICA_REQUEST_RESPONSE = 0x04, + FELICA_READ_WITHOUT_ENC = 0x06, + FELICA_WRITE_WITHOUT_ENC = 0x08, + FELICA_REQUEST_SYSTEM_CODE = 0x0c +}; + +enum FeliCaRequestCode : uint8_t { + FELICA_REQ_CODE_NONE = 0x00, + FELICA_REQ_CODE_SYSTEM_CODE = 0x01, + FELICA_REQ_CODE_COMMUNICATION = 0x00 +}; + +/* PN532 packet structures */ + +static constexpr uint8_t PN532_PACKET_PREAMBLE = 0x55; +static constexpr uint8_t PN532_PACKET_START1 = 0x00; +static constexpr uint8_t PN532_PACKET_START2 = 0xff; + +enum PN532PacketAddress : uint8_t { + PN532_ADDR_ERROR = 0x7f, + PN532_ADDR_DEVICE = 0xd4, + PN532_ADDR_HOST = 0xd5 +}; + +class PN532PacketHeader { +public: + uint8_t preamble, startCode[2]; + uint8_t length, lengthChecksum; + uint8_t address; + + inline size_t getPacketLength(void) const { + return (sizeof(PN532PacketHeader) - 1) + length + 2; + } + inline uint8_t *getData(void) { + return reinterpret_cast(this + 1); + } + + void updateChecksum(void); + bool validateChecksum(void) const; + + void encodeCommand(int paramLength); + bool decodeResponse(void) const; + bool isErrorResponse(void) const; +}; + +template class PN532Packet : public PN532PacketHeader { +public: + uint8_t command; + uint8_t param[N]; +}; + +class PN532ExtPacketHeader { +public: + uint8_t preamble, startCode[2]; + uint8_t packetMagic[2]; + uint8_t length[2], lengthChecksum; + uint8_t address; + + inline size_t getDataLength(void) const { + return util::concat2(length[1], length[0]); + } + inline size_t getPacketLength(void) const { + return (sizeof(PN532PacketHeader) - 1) + getDataLength() + 2; + } + inline uint8_t *getData(void) { + return reinterpret_cast(this + 1); + } + + void updateChecksum(void); + bool validateChecksum(void) const; + + void encodeCommand(int paramLength); + bool decodeResponse(void) const; +}; + +template class PN532ExtPacket : public PN532ExtPacketHeader { +public: + uint8_t command; + uint8_t param[N]; +}; + +class PN532ACKPacket { +public: + uint8_t preamble, startCode[2]; + uint8_t packetMagic[2]; + uint8_t postamble; + + void encodeACK(bool isACK = true); + bool decodeACK(void) const; +}; + +/* PN532 driver */ + +class PN532Driver { +private: + io::UARTDriver &_serial; + bool _isIdle; + + template inline bool _transact( + const PN532PacketHeader &request, + PN532Packet &response + ) { + _transact(request, response, sizeof(response)); + } + + bool _transact( + const PN532PacketHeader &request, + PN532PacketHeader &response, + size_t maxRespLength + ); + +public: + inline PN532Driver(io::UARTDriver &serial) + : _serial(serial) {} + + bool init(void); + bool setMaxRetries(int count); + bool goIdle(void); + + size_t readISO14443CardID(uint8_t *output); + size_t readFeliCaCardID(uint8_t *output, uint16_t systemCode = 0xffff); +}; + +} diff --git a/src/main/uibase.cpp b/src/main/uibase.cpp index f831fd0..2e1e7e6 100644 --- a/src/main/uibase.cpp +++ b/src/main/uibase.cpp @@ -282,14 +282,24 @@ void TextOverlay::draw(Context &ctx, bool active) const { if (leftText) { rect.x = 8; rect.w = ctx.gpuCtx.width - 16; - ctx.font.draw(ctx.gpuCtx, leftText, rect, ctx.colors[COLOR_TEXT2]); + ctx.font.draw( + ctx.gpuCtx, + leftText, + rect, + ctx.colors[COLOR_TEXT2] + ); } if (rightText) { int width = ctx.font.getStringWidth(rightText); rect.x = ctx.gpuCtx.width - (8 + width); rect.w = width; - ctx.font.draw(ctx.gpuCtx, rightText, rect, ctx.colors[COLOR_TEXT2]); + ctx.font.draw( + ctx.gpuCtx, + rightText, + rect, + ctx.colors[COLOR_TEXT2] + ); } } @@ -302,7 +312,8 @@ void SplashOverlay::draw(Context &ctx, bool active) const { // Backdrop _newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height); ctx.gpuCtx.drawBackdrop( - gp0_rgb(brightness, brightness, brightness), GP0_BLEND_SUBTRACT + gp0_rgb(brightness, brightness, brightness), + GP0_BLEND_SUBTRACT ); if (brightness < 0xff) @@ -337,7 +348,10 @@ void LogOverlay::draw(Context &ctx, bool active) const { // Backdrop _newLayer( - ctx, 0, offset - ctx.gpuCtx.height, ctx.gpuCtx.width, + ctx, + 0, + offset - ctx.gpuCtx.height, + ctx.gpuCtx.width, ctx.gpuCtx.height ); ctx.gpuCtx.drawBackdrop(ctx.colors[COLOR_BACKDROP], GP0_BLEND_SUBTRACT); @@ -355,7 +369,10 @@ void LogOverlay::draw(Context &ctx, bool active) const { for (int i = (screenHeight / lineHeight) - 1; i >= 0; i--) { ctx.font.draw( - ctx.gpuCtx, _buffer.getLine(i), rect, ctx.colors[COLOR_TEXT1] + ctx.gpuCtx, + _buffer.getLine(i), + rect, + ctx.colors[COLOR_TEXT1] ); rect.y1 = rect.y2; @@ -378,7 +395,8 @@ void ScreenshotOverlay::draw(Context &ctx, bool active) const { _newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height); ctx.gpuCtx.drawBackdrop( - gp0_rgb(brightness, brightness, brightness), GP0_BLEND_ADD + gp0_rgb(brightness, brightness, brightness), + GP0_BLEND_ADD ); } @@ -423,7 +441,8 @@ void BackdropScreen::draw(Context &ctx, bool active) const { _newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height); ctx.gpuCtx.drawBackdrop( - gp0_rgb(brightness, brightness, brightness), GP0_BLEND_ADD + gp0_rgb(brightness, brightness, brightness), + GP0_BLEND_ADD ); } @@ -443,28 +462,47 @@ void ModalScreen::draw(Context &ctx, bool active) const { int windowHeight = TITLE_BAR_HEIGHT + _height; _newLayer( - ctx, (ctx.gpuCtx.width - _width) / 2, - (ctx.gpuCtx.height - windowHeight) / 2, _width + SHADOW_OFFSET, + ctx, + (ctx.gpuCtx.width - _width) / 2, + (ctx.gpuCtx.height - windowHeight) / 2, + _width + SHADOW_OFFSET, windowHeight + SHADOW_OFFSET ); _setBlendMode(ctx, GP0_BLEND_SEMITRANS, true); // Window ctx.gpuCtx.drawGradientRectD( - 0, 0, _width, windowHeight, ctx.colors[COLOR_WINDOW1], - ctx.colors[COLOR_WINDOW2], ctx.colors[COLOR_WINDOW3] + 0, + 0, + _width, + windowHeight, + ctx.colors[COLOR_WINDOW1], + ctx.colors[COLOR_WINDOW2], + ctx.colors[COLOR_WINDOW3] ); ctx.gpuCtx.drawGradientRectH( - 0, 0, _titleBarAnim.getValue(ctx.time), TITLE_BAR_HEIGHT, - ctx.colors[COLOR_ACCENT1], ctx.colors[COLOR_ACCENT2] + 0, + 0, + _titleBarAnim.getValue(ctx.time), + TITLE_BAR_HEIGHT, + ctx.colors[COLOR_ACCENT1], + ctx.colors[COLOR_ACCENT2] ); ctx.gpuCtx.drawRect( - _width, SHADOW_OFFSET, SHADOW_OFFSET, windowHeight, - ctx.colors[COLOR_SHADOW], true + _width, + SHADOW_OFFSET, + SHADOW_OFFSET, + windowHeight, + ctx.colors[COLOR_SHADOW], + true ); ctx.gpuCtx.drawRect( - SHADOW_OFFSET, windowHeight, _width - SHADOW_OFFSET, SHADOW_OFFSET, - ctx.colors[COLOR_SHADOW], true + SHADOW_OFFSET, + windowHeight, + _width - SHADOW_OFFSET, + SHADOW_OFFSET, + ctx.colors[COLOR_SHADOW], + true ); // Text @@ -474,11 +512,22 @@ void ModalScreen::draw(Context &ctx, bool active) const { rect.y1 = TITLE_BAR_PADDING; rect.x2 = _width - TITLE_BAR_PADDING; rect.y2 = TITLE_BAR_PADDING + ctx.font.getLineHeight(); - ctx.font.draw(ctx.gpuCtx, _title, rect, ctx.colors[COLOR_TITLE]); + ctx.font.draw( + ctx.gpuCtx, + _title, + rect, + ctx.colors[COLOR_TITLE] + ); rect.y1 = TITLE_BAR_HEIGHT + MODAL_PADDING; rect.y2 = _height - MODAL_PADDING; - ctx.font.draw(ctx.gpuCtx, _body, rect, ctx.colors[COLOR_TEXT1], true); + ctx.font.draw( + ctx.gpuCtx, + _body, + rect, + ctx.colors[COLOR_TEXT1], + true + ); } } diff --git a/src/main/uicommon.cpp b/src/main/uicommon.cpp index 95bc0a4..9ab0cae 100644 --- a/src/main/uicommon.cpp +++ b/src/main/uicommon.cpp @@ -42,7 +42,11 @@ void TextScreen::draw(Context &ctx, bool active) const { // Top/bottom text _newLayer( - ctx, SCREEN_MARGIN_X, SCREEN_MARGIN_Y, screenWidth, screenHeight + ctx, + SCREEN_MARGIN_X, + SCREEN_MARGIN_Y, + screenWidth, + screenHeight ); gpu::Rect rect; @@ -51,11 +55,22 @@ void TextScreen::draw(Context &ctx, bool active) const { rect.y1 = 0; rect.x2 = screenWidth; rect.y2 = lineHeight; - ctx.font.draw(ctx.gpuCtx, _title, rect, ctx.colors[COLOR_TITLE]); + ctx.font.draw( + ctx.gpuCtx, + _title, + rect, + ctx.colors[COLOR_TITLE] + ); rect.y1 = screenHeight - SCREEN_PROMPT_HEIGHT_MIN; rect.y2 = screenHeight; - ctx.font.draw(ctx.gpuCtx, _prompt, rect, ctx.colors[COLOR_TEXT1], true); + ctx.font.draw( + ctx.gpuCtx, + _prompt, + rect, + ctx.colors[COLOR_TEXT1], + true + ); int bodyOffset = lineHeight + SCREEN_BLOCK_MARGIN; int bodyHeight = screenHeight - @@ -63,7 +78,10 @@ void TextScreen::draw(Context &ctx, bool active) const { // Scrollable text _newLayer( - ctx, SCREEN_MARGIN_X, SCREEN_MARGIN_Y + bodyOffset, screenWidth, + ctx, + SCREEN_MARGIN_X, + SCREEN_MARGIN_Y + bodyOffset, + screenWidth, bodyHeight ); @@ -75,7 +93,14 @@ void TextScreen::draw(Context &ctx, bool active) const { clip.y1 = 0; clip.x2 = screenWidth; clip.y2 = bodyHeight; - ctx.font.draw(ctx.gpuCtx, _body, rect, clip, ctx.colors[COLOR_TEXT1], true); + ctx.font.draw( + ctx.gpuCtx, + _body, + rect, + clip, + ctx.colors[COLOR_TEXT1], + true + ); } void TextScreen::update(Context &ctx) { @@ -146,14 +171,22 @@ void ImageScreen::draw(Context &ctx, bool active) const { int _height = height + _imagePadding; ctx.gpuCtx.drawRect( - x - _width, y - _height, _width * 2, _height * 2, _backdropColor + x - _width, + y - _height, + _width * 2, + _height * 2, + _backdropColor ); } // Image if (_imageScale > 1) _image->drawScaled( - ctx.gpuCtx, x - width - 1, y - height - 1, width * 2, height * 2 + ctx.gpuCtx, + x - width - 1, + y - height - 1, + width * 2, + height * 2 ); else _image->draw(ctx.gpuCtx, x - width, y - height); @@ -166,11 +199,22 @@ void ImageScreen::draw(Context &ctx, bool active) const { rect.y1 = SCREEN_MARGIN_Y; rect.x2 = ctx.gpuCtx.width - SCREEN_MARGIN_X; rect.y2 = SCREEN_MARGIN_Y + lineHeight; - ctx.font.draw(ctx.gpuCtx, _title, rect, ctx.colors[COLOR_TITLE]); + ctx.font.draw( + ctx.gpuCtx, + _title, + rect, + ctx.colors[COLOR_TITLE] + ); rect.y1 = ctx.gpuCtx.height - (SCREEN_MARGIN_Y + SCREEN_PROMPT_HEIGHT); rect.y2 = ctx.gpuCtx.height - SCREEN_MARGIN_Y; - ctx.font.draw(ctx.gpuCtx, _prompt, rect, ctx.colors[COLOR_TEXT1], true); + ctx.font.draw( + ctx.gpuCtx, + _prompt, + rect, + ctx.colors[COLOR_TEXT1], + true + ); } ListScreen::ListScreen(void) @@ -196,25 +240,37 @@ void ListScreen::_drawItems(Context &ctx) const { if ((itemY + itemHeight) >= 0) { if (i == _activeItem) { ctx.gpuCtx.drawRect( - LIST_BOX_PADDING, itemY, itemWidth, itemHeight, + LIST_BOX_PADDING, + itemY, + itemWidth, + itemHeight, ctx.colors[COLOR_HIGHLIGHT2] ); ctx.gpuCtx.drawRect( - LIST_BOX_PADDING, itemY, _itemAnim.getValue(ctx.time), - itemHeight, ctx.colors[COLOR_HIGHLIGHT1] + LIST_BOX_PADDING, + itemY, + _itemAnim.getValue(ctx.time), + itemHeight, + ctx.colors[COLOR_HIGHLIGHT1] ); rect.y1 = itemY + LIST_ITEM_PADDING + lineHeight; rect.y2 = rect.y1 + lineHeight; ctx.font.draw( - ctx.gpuCtx, _itemPrompt, rect, ctx.colors[COLOR_SUBTITLE] + ctx.gpuCtx, + _itemPrompt, + rect, + ctx.colors[COLOR_SUBTITLE] ); } rect.y1 = itemY + LIST_ITEM_PADDING; rect.y2 = rect.y1 + lineHeight; ctx.font.draw( - ctx.gpuCtx, _getItemName(ctx, i), rect, ctx.colors[COLOR_TITLE] + ctx.gpuCtx, + _getItemName(ctx, i), + rect, + ctx.colors[COLOR_TITLE] ); } @@ -237,7 +293,11 @@ void ListScreen::draw(Context &ctx, bool active) const { int lineHeight = ctx.font.getLineHeight(); _newLayer( - ctx, SCREEN_MARGIN_X, SCREEN_MARGIN_Y, screenWidth, screenHeight + ctx, + SCREEN_MARGIN_X, + SCREEN_MARGIN_Y, + screenWidth, + screenHeight ); // Text @@ -247,25 +307,46 @@ void ListScreen::draw(Context &ctx, bool active) const { rect.y1 = 0; rect.x2 = screenWidth; rect.y2 = lineHeight; - ctx.font.draw(ctx.gpuCtx, _title, rect, ctx.colors[COLOR_TITLE]); + ctx.font.draw( + ctx.gpuCtx, + _title, + rect, + ctx.colors[COLOR_TITLE] + ); rect.y1 = screenHeight - SCREEN_PROMPT_HEIGHT; rect.y2 = screenHeight; - ctx.font.draw(ctx.gpuCtx, _prompt, rect, ctx.colors[COLOR_TEXT1], true); + ctx.font.draw( + ctx.gpuCtx, + _prompt, + rect, + ctx.colors[COLOR_TEXT1], + true + ); _newLayer( - ctx, SCREEN_MARGIN_X, - SCREEN_MARGIN_Y + lineHeight + SCREEN_BLOCK_MARGIN, screenWidth, + ctx, + SCREEN_MARGIN_X, + SCREEN_MARGIN_Y + lineHeight + SCREEN_BLOCK_MARGIN, + screenWidth, listHeight ); _setBlendMode(ctx, GP0_BLEND_SEMITRANS, true); // List box ctx.gpuCtx.drawRect( - 0, 0, screenWidth / 2, listHeight, ctx.colors[COLOR_BOX1] + 0, + 0, + screenWidth / 2, + listHeight, + ctx.colors[COLOR_BOX1] ); ctx.gpuCtx.drawGradientRectH( - screenWidth / 2, 0, screenWidth / 2, listHeight, ctx.colors[COLOR_BOX1], + screenWidth / 2, + 0, + screenWidth / 2, + listHeight, + ctx.colors[COLOR_BOX1], ctx.colors[COLOR_BOX2] ); @@ -282,13 +363,19 @@ void ListScreen::draw(Context &ctx, bool active) const { if (_activeItem) { iconRect.y = LIST_BOX_PADDING; ctx.font.draw( - ctx.gpuCtx, CH_UP_ARROW, iconRect, ctx.colors[COLOR_TEXT1] + ctx.gpuCtx, + CH_UP_ARROW, + iconRect, + ctx.colors[COLOR_TEXT1] ); } if (_activeItem < (_listLength - 1)) { iconRect.y = listHeight - (lineHeight + LIST_BOX_PADDING); ctx.font.draw( - ctx.gpuCtx, CH_DOWN_ARROW, iconRect, ctx.colors[COLOR_TEXT1] + ctx.gpuCtx, + CH_DOWN_ARROW, + iconRect, + ctx.colors[COLOR_TEXT1] ); } } @@ -335,9 +422,17 @@ void ListScreen::update(Context &ctx) { int currentOffset = -_scrollAnim.getTargetValue(); if (topOffset < currentOffset) - _scrollAnim.setValue(ctx.time, LIST_BOX_PADDING - topOffset, SPEED_FAST); + _scrollAnim.setValue( + ctx.time, + LIST_BOX_PADDING - topOffset, + SPEED_FAST + ); else if (bottomOffset > currentOffset) - _scrollAnim.setValue(ctx.time, -(LIST_BOX_PADDING + bottomOffset), SPEED_FAST); + _scrollAnim.setValue( + ctx.time, + -(LIST_BOX_PADDING + bottomOffset), + SPEED_FAST + ); } } diff --git a/src/main/uimodals.cpp b/src/main/uimodals.cpp index 8450b5b..986ed02 100644 --- a/src/main/uimodals.cpp +++ b/src/main/uimodals.cpp @@ -59,32 +59,51 @@ void MessageBoxScreen::draw(Context &ctx, bool active) const { if (_locked) { ctx.gpuCtx.drawRect( - buttonX, buttonY, rect.w, BUTTON_HEIGHT, - ctx.colors[COLOR_SHADOW], true + buttonX, + buttonY, + rect.w, + BUTTON_HEIGHT, + ctx.colors[COLOR_SHADOW], + true ); ctx.font.draw( - ctx.gpuCtx, _buttons[i], rect, ctx.colors[COLOR_TEXT2] + ctx.gpuCtx, + _buttons[i], + rect, + ctx.colors[COLOR_TEXT2] ); } else { if (i == activeButton) { ctx.gpuCtx.drawRect( - buttonX, buttonY, rect.w, BUTTON_HEIGHT, + buttonX, + buttonY, + rect.w, + BUTTON_HEIGHT, ctx.colors[COLOR_HIGHLIGHT2] ); ctx.gpuCtx.drawRect( - buttonX, buttonY, _buttonAnim.getValue(ctx.time), - BUTTON_HEIGHT, ctx.colors[COLOR_HIGHLIGHT1] + buttonX, + buttonY, + _buttonAnim.getValue(ctx.time), + BUTTON_HEIGHT, + ctx.colors[COLOR_HIGHLIGHT1] ); } else { ctx.gpuCtx.drawRect( - buttonX, buttonY, rect.w, BUTTON_HEIGHT, + buttonX, + buttonY, + rect.w, + BUTTON_HEIGHT, ctx.colors[COLOR_WINDOW3] ); } ctx.font.draw( - ctx.gpuCtx, _buttons[i], rect, ctx.colors[COLOR_TITLE] + ctx.gpuCtx, + _buttons[i], + rect, + ctx.colors[COLOR_TITLE] ); } @@ -152,13 +171,17 @@ void HexEntryScreen::draw(Context &ctx, bool active) const { if (!active) return; - int boxY = TITLE_BAR_HEIGHT + _height - - (BUTTON_HEIGHT + MODAL_PADDING) * 2; + int boxY = + TITLE_BAR_HEIGHT + _height - (BUTTON_HEIGHT + MODAL_PADDING) * 2; int boxWidth = _width - MODAL_PADDING * 2; // Text box ctx.gpuCtx.drawRect( - MODAL_PADDING, boxY, boxWidth, BUTTON_HEIGHT, ctx.colors[COLOR_BOX1] + MODAL_PADDING, + boxY, + boxWidth, + BUTTON_HEIGHT, + ctx.colors[COLOR_BOX1] ); char string[128]; @@ -173,8 +196,11 @@ void HexEntryScreen::draw(Context &ctx, bool active) const { if (_activeButton < _buttonIndexOffset) ctx.gpuCtx.drawGradientRectV( stringOffset + _cursorAnim.getValue(ctx.time), - boxY + BUTTON_HEIGHT / 2, _charWidth, BUTTON_HEIGHT / 2, - ctx.colors[COLOR_BOX1], ctx.colors[COLOR_HIGHLIGHT1] + boxY + BUTTON_HEIGHT / 2, + _charWidth, + BUTTON_HEIGHT / 2, + ctx.colors[COLOR_BOX1], + ctx.colors[COLOR_HIGHLIGHT1] ); // Current string @@ -182,7 +208,12 @@ void HexEntryScreen::draw(Context &ctx, bool active) const { rect.y1 = boxY + BUTTON_PADDING; rect.x2 = _width - MODAL_PADDING; rect.y2 = boxY + BUTTON_PADDING + ctx.font.getLineHeight(); - ctx.font.draw(ctx.gpuCtx, string, rect, ctx.colors[COLOR_TITLE]); + ctx.font.draw( + ctx.gpuCtx, + string, + rect, + ctx.colors[COLOR_TITLE] + ); // Highlighted field if (_activeButton < _buttonIndexOffset) { @@ -190,7 +221,12 @@ void HexEntryScreen::draw(Context &ctx, bool active) const { ptr[1] = 0; rect.x1 = stringOffset + _cursorAnim.getTargetValue(); - ctx.font.draw(ctx.gpuCtx, ptr, rect, ctx.colors[COLOR_SUBTITLE]); + ctx.font.draw( + ctx.gpuCtx, + ptr, + rect, + ctx.colors[COLOR_SUBTITLE] + ); } } @@ -314,13 +350,17 @@ void DateEntryScreen::draw(Context &ctx, bool active) const { if (!active) return; - int boxY = TITLE_BAR_HEIGHT + _height - - (BUTTON_HEIGHT + MODAL_PADDING) * 2; + int boxY = + TITLE_BAR_HEIGHT + _height - (BUTTON_HEIGHT + MODAL_PADDING) * 2; int boxWidth = _width - MODAL_PADDING * 2; // Text box ctx.gpuCtx.drawRect( - MODAL_PADDING, boxY, boxWidth, BUTTON_HEIGHT, ctx.colors[COLOR_BOX1] + MODAL_PADDING, + boxY, + boxWidth, + BUTTON_HEIGHT, + ctx.colors[COLOR_BOX1] ); char string[24]; @@ -342,8 +382,10 @@ void DateEntryScreen::draw(Context &ctx, bool active) const { if (_activeButton < _buttonIndexOffset) ctx.gpuCtx.drawGradientRectV( stringOffset + _cursorAnim.getValue(ctx.time), - boxY + BUTTON_HEIGHT / 2, _charWidth * fieldLength, - BUTTON_HEIGHT / 2,ctx.colors[COLOR_BOX1], + boxY + BUTTON_HEIGHT / 2, + _charWidth * fieldLength, + BUTTON_HEIGHT / 2, + ctx.colors[COLOR_BOX1], ctx.colors[COLOR_HIGHLIGHT1] ); @@ -352,7 +394,12 @@ void DateEntryScreen::draw(Context &ctx, bool active) const { rect.y1 = boxY + BUTTON_PADDING; rect.x2 = _width - MODAL_PADDING; rect.y2 = boxY + BUTTON_PADDING + ctx.font.getLineHeight(); - ctx.font.draw(ctx.gpuCtx, string, rect, ctx.colors[COLOR_TITLE]); + ctx.font.draw( + ctx.gpuCtx, + string, + rect, + ctx.colors[COLOR_TITLE] + ); // Highlighted field if (_activeButton < _buttonIndexOffset) { @@ -360,7 +407,12 @@ void DateEntryScreen::draw(Context &ctx, bool active) const { ptr[fieldLength] = 0; rect.x1 = stringOffset + _cursorAnim.getTargetValue(); - ctx.font.draw(ctx.gpuCtx, ptr, rect, ctx.colors[COLOR_SUBTITLE]); + ctx.font.draw( + ctx.gpuCtx, + ptr, + rect, + ctx.colors[COLOR_SUBTITLE] + ); } } @@ -422,7 +474,9 @@ void DateEntryScreen::update(Context &ctx) { // Update the cursor's position if necessary. if (oldActive != _activeButton) _cursorAnim.setValue( - ctx.time, _fieldOffsets[_activeButton], SPEED_FASTEST + ctx.time, + _fieldOffsets[_activeButton], + SPEED_FASTEST ); } } @@ -430,6 +484,17 @@ void DateEntryScreen::update(Context &ctx) { ProgressScreen::ProgressScreen(void) : ModalScreen(MODAL_WIDTH, MODAL_HEIGHT_REDUCED) {} +void ProgressScreen::_setProgress(Context &ctx, int part, int total) { + if (!total) + total = 1; + + int totalWidth = _width - MODAL_PADDING * 2; + int partWidth = (totalWidth * part) / total; + + if (_progressBarAnim.getTargetValue() != partWidth) + _progressBarAnim.setValue(ctx.time, partWidth, SPEED_FASTEST); +} + void ProgressScreen::show(Context &ctx, bool goBack) { ModalScreen::show(ctx, goBack); @@ -445,17 +510,25 @@ void ProgressScreen::draw(Context &ctx, bool active) const { int fullBarWidth = _width - MODAL_PADDING * 2; int barX = (_width - fullBarWidth) / 2; - int barY = TITLE_BAR_HEIGHT + _height - - (PROGRESS_BAR_HEIGHT + MODAL_PADDING); + int barY = + TITLE_BAR_HEIGHT + _height - (PROGRESS_BAR_HEIGHT + MODAL_PADDING); _setBlendMode(ctx, GP0_BLEND_SEMITRANS, true); ctx.gpuCtx.drawRect( - barX, barY, fullBarWidth, PROGRESS_BAR_HEIGHT, ctx.colors[COLOR_WINDOW3] + barX, + barY, + fullBarWidth, + PROGRESS_BAR_HEIGHT, + ctx.colors[COLOR_WINDOW3] ); ctx.gpuCtx.drawGradientRectH( - barX, barY, _progressBarAnim.getValue(ctx.time), PROGRESS_BAR_HEIGHT, - ctx.colors[COLOR_PROGRESS2], ctx.colors[COLOR_PROGRESS1] + barX, + barY, + _progressBarAnim.getValue(ctx.time), + PROGRESS_BAR_HEIGHT, + ctx.colors[COLOR_PROGRESS2], + ctx.colors[COLOR_PROGRESS1] ); } diff --git a/src/main/uimodals.hpp b/src/main/uimodals.hpp index 61a69ce..a2b31dc 100644 --- a/src/main/uimodals.hpp +++ b/src/main/uimodals.hpp @@ -86,16 +86,7 @@ private: util::Tween _progressBarAnim; protected: - inline void _setProgress(Context &ctx, int part, int total) { - if (!total) - total = 1; - - int totalWidth = _width - MODAL_PADDING * 2; - int partWidth = (totalWidth * part) / total; - - if (_progressBarAnim.getTargetValue() != partWidth) - _progressBarAnim.setValue(ctx.time, partWidth, SPEED_FASTEST); - } + void _setProgress(Context &ctx, int part, int total); public: ProgressScreen(void);