mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-03-01 07:20:42 +01:00
UI code reformatting, add PN532 NFC driver
Some checks failed
Build / Run build (push) Failing after 5m49s
Some checks failed
Build / Run build (push) Failing after 5m49s
This commit is contained in:
parent
7faa1612e0
commit
4667feb240
@ -70,6 +70,7 @@ set(
|
||||
set(
|
||||
exthwSources
|
||||
src/common/exthw/lcd.cpp
|
||||
src/common/exthw/nfc.cpp
|
||||
)
|
||||
set(
|
||||
fsSources
|
||||
|
@ -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"
|
||||
|
@ -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 {
|
||||
|
411
src/common/exthw/nfc.cpp
Normal file
411
src/common/exthw/nfc.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
228
src/common/exthw/nfc.hpp
Normal file
228
src/common/exthw/nfc.hpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#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<uint8_t *>(this + 1);
|
||||
}
|
||||
|
||||
void updateChecksum(void);
|
||||
bool validateChecksum(void) const;
|
||||
|
||||
void encodeCommand(int paramLength);
|
||||
bool decodeResponse(void) const;
|
||||
bool isErrorResponse(void) const;
|
||||
};
|
||||
|
||||
template<size_t N> 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<uint8_t *>(this + 1);
|
||||
}
|
||||
|
||||
void updateChecksum(void);
|
||||
bool validateChecksum(void) const;
|
||||
|
||||
void encodeCommand(int paramLength);
|
||||
bool decodeResponse(void) const;
|
||||
};
|
||||
|
||||
template<size_t N> 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<size_t N> inline bool _transact(
|
||||
const PN532PacketHeader &request,
|
||||
PN532Packet<N> &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);
|
||||
};
|
||||
|
||||
}
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -86,16 +86,7 @@ private:
|
||||
util::Tween<int, util::QuadOutEasing> _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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user