cake
/
libp7
Archived
1
0
Fork 1
This repository has been archived on 2024-03-16. You can view files and clone it, but cannot push or open issues or pull requests.
libp7/src/protocol/seven/recv.c

383 lines
10 KiB
C

/* *****************************************************************************
* protocol/seven/recv.c -- receive and decode the packet.
* Copyright (C) 2016-2017 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
*
* This file is part of libp7.
* libp7 is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3.0 of the License,
* or (at your option) any later version.
*
* libp7 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libp7; if not, see <http://www.gnu.org/licenses/>.
*
* These functions are about receiving and decoding a packet.
* It is also about managing checksum problems and timeouts.
* ************************************************************************** */
#include <libp7/internals.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(1)
/* TYPZ1/2 subheader */
struct typz_subheader {
/* ASCII-hex size */
uint8_t size[6];
/* dimensions */
uint8_t height[4];
uint8_t width[4];
/* one: we are number one but it is in ascii (always "1") */
uint8_t one;
/* encoding algorithm:
* - RC2: 16-bit mode;
* - RC3: 3-bit mode (1 nib./pxl, red-green-blue-trash)
* - RM2: 2-bit mode? */
uint8_t enc[3];
};
#pragma pack()
/**
* complete_packet:
* Complete the packet with N bytes.
*
* Uses stream buffering to achieve this.
*
* @arg handle the libp7 handle.
* @arg buf the buffer.
* @arg recv pointer to currently received bytes.
* @arg with complete with this number of byte.
* @return the error (0 if ok)
*/
static int complete_packet(p7_handle_t *handle,
unsigned char *buf, size_t *recv, size_t with)
{
int err = p7_read(&handle->_stream, &buf[*recv], with);
*recv += with;
return (err);
}
/**
* p7_recv_main:
* Receive a packet.
*
* This is the main receiving function. It receives, parses, and returns
* if everything has been successfully done or not.
* It uses buffered I/O to read progressively all of the packet.
*
* @arg handle the libp7 handler
* @return the error (0 if ok)
*/
#define buffer handle->_recv_buffer
#define COMPLETE_PACKET(N) { \
int COMP_PACKET_err; \
if ((COMP_PACKET_err = complete_packet(handle, \
buffer, &received, (N)))) \
return (COMP_PACKET_err); \
}
static int p7_recv_main(p7_handle_t *handle)
{
/* prepare reception */
size_t received = 0;
/* log packet */
log_info("receiving packet...");
/* get first three bytes, check if is CAL */
do {
COMPLETE_PACKET(3)
if (memcmp(buffer, "CAL", 3))
break;
/* get the CAL value */
log_info("is the magic CAL!");
COMPLETE_PACKET(4) /* TODO: check if is 00D0, and what is it? */
log_info("CAL value is %.4s", &buffer[3]);
handle->_flags |= p7_intflag_nocheck;
/* good as new! */
received = 0;
log_info("Restart receiving the packet.");
} while (1);
/* adjust */
if (handle->_flags & p7_intflag_adjscreen) {
/* adjust. */
log_info("Adjusting: looking for the screen header.");
log_info("Current buffer:");
logm_info(buffer, 3);
while (memcmp(buffer, "\x0BTY", 3)) {
buffer[0] = buffer[1];
buffer[1] = buffer[2];
received = 2;
COMPLETE_PACKET(1)
log_info("Current buffer:");
logm_info(buffer, 3);
}
}
/* get the type */
response.type = buffer[0];
/* image has a particular format starting from here, look for it now! */
if (response.type == p7_pt_ohp) {
log_info("Johnson, this is a screen.");
COMPLETE_PACKET(3)
/* get image by its type */
unsigned int image_size = 0;
if (!memcmp(&buffer[1], "TYP01", 5)) {
log_info("This is normal VRAM, we know it !");
response.pictype = p7_pictype_1bit;
response.width = 128;
response.height = 64;
image_size = 1024;
} else if (!memcmp(&buffer[1], "TYPZ1", 5)
|| !memcmp(&buffer[1], "TYPZ2", 5)) {
log_info("Prizm VRAM!");
COMPLETE_PACKET(sizeof(struct typz_subheader))
struct typz_subheader *s = (void*)&buffer[6];
response.width = p7_getascii(s->width, 4);
response.height = p7_getascii(s->height, 4);
/* check the type */
response.pictype = 0;
if (!memcmp(s->enc, "RC2", 3))
response.pictype = p7_pictype_16bit;
else if (!memcmp(s->enc, "RC3", 3))
response.pictype = p7_pictype_3bit;
else if (!memcmp(s->enc, "RM2", 3))
response.pictype = p7_pictype_2bit;
#if LOGLEVEL <= ll_error
else log_error("Unknown encoding: %.3s", s->enc);
#endif
image_size = p7_getascii(s->size, 6);
log_info("Image size: %uB", image_size);
handle->_flags |= p7_intflag_nocheck;
} else {
log_error("Unknown picture encoding: %.5s", &buffer[1]);
return (p7_error_checksum);
}
/* complete packet */
size_t hdsize = received;
log_info("Get screen content (%uo)", image_size);
COMPLETE_PACKET(image_size)
memcpy(&response.vram, &buffer[hdsize], image_size);
/* log */
unsigned int packet_size = hdsize + image_size;
log_info("received the following [screen] packet (%uo) :", packet_size);
logm_info(buffer, packet_size);
/* check the sum */
if ((~handle->_flags & p7_intflag_nocheck)) {
COMPLETE_PACKET(2)
if (p7_checksum(buffer, packet_size)
!= p7_getascii(&buffer[packet_size], 2))
return (p7_error_checksum);
}
handle->_flags &= ~p7_intflag_nocheck;
/* and return the packet */
return (0);
}
/* subtype and extended */
COMPLETE_PACKET(1)
p7ushort_t subtype = p7_getascii(&buffer[1], 2);
/* - extended (beginning) - */
p7ushort_t data_size = 0;
int is_extended = (buffer[3] == '1');
if (is_extended) {
/* get data size */
COMPLETE_PACKET(4)
data_size = p7_getascii(&buffer[4], 4);
/* get data */
COMPLETE_PACKET(data_size + 2)
} else
COMPLETE_PACKET(2)
/* check if we should read a binary zero */
int is_binary_zero = (response.type == p7_pt_cmd
&& subtype == 0x56);
if (is_binary_zero) COMPLETE_PACKET(1)
/* log */
p7ushort_t packet_size = 6 + 4 * !!is_extended + data_size;
log_info("received the following [normal] packet (%" PRIuP7SHORT "o) :",
packet_size);
logm_info(buffer, packet_size);
/* calculate checksum */
unsigned int calculated_checksum = p7_checksum(buffer, packet_size);
unsigned int found_checksum = p7_getascii(&buffer[packet_size - 2], 2);
if (calculated_checksum != found_checksum)
return (p7_error_checksum);
/* - extended (finish) - */
if (is_extended)
data_size = p7_decode(&buffer[8], &buffer[8], data_size);
/* get fields out for specific packets */
switch (response.type) {
/* - for command - */
case p7_pt_cmd:
log_info("packet was interpreted as a command one");
response.code = subtype;
log_info("command is '%s' (0x%02" PRIxP7SHORT ")",
p7_getcmdstring(subtype), subtype);
if (is_extended
&& p7_decode_command(handle, &buffer[8], data_size))
return (p7_error_checksum);
break;
/* - for data - */
case p7_pt_data:
log_info("packet was interpreted as a data one");
p7_decode_data(handle, &buffer[8], data_size);
break;
/* - for roleswap - */
case p7_pt_roleswp:
log_info("packet was interpreted as a roleswap one");
log_info("becoming active again");
handle->_flags |= p7_intflag_active;
break;
/* - for check - */
case p7_pt_check:
log_info("packet was interpreted as a check one");
response.initial = !subtype;
break;
/* - for ack - */
case p7_pt_ack:
log_info("packet was interpreted as an ack one");
response.ow = (subtype == 0x01);
response.extended = (subtype == 0x02);
if (response.extended
&& p7_decode_ack(handle, &buffer[8], data_size))
return (p7_error_checksum);
if (subtype == 0x03)
handle->_flags |= p7_intflag_terminated;
break;
/* - for error - */
case p7_pt_error:
log_info("packet was interpreted as an error one");
log_info("error is '%s' (0x%02" PRIxP7SHORT ")",
p7_geterrstring(subtype), subtype);
response.code = subtype;
if (subtype == p7_err_fullmem)
handle->_flags |= p7_intflag_terminated;
break;
/* - for termination - */
case p7_pt_terminate:
log_info("packet was interpreted as a terminate one");
log_info("termination cause is '%s' (0x%02" PRIxP7SHORT ")",
p7_gettermstring(subtype), subtype);
response.code = subtype;
if (subtype == p7_term_user)
return (p7_error_interrupted);
break;
/* other */
default:
log_fatal("bad type received ! type was 0x%02x", response.type);
return (p7_error_unknown);
}
/* finally, return the packet */
return (0);
}
/**
* p7_recv:
* Receives packet, checks for errors.
*
* @arg handle the libp7 handler
* @arg checksum if 0 and invalid checksum, ignore and receive again
* @return if it worked
*/
int p7_recv(p7_handle_t *handle, int checksum)
{
/* check if handler is initialized */
if (!handle)
return (p7_error_uninitialized);
/* main receiving loop */
int tries = 2, err = 0;
int wasresend = 0;
do {
/* get packet */
err = p7_recv_main(handle);
/* if there was a fail */
if (err) switch (err) {
/* if calc couldn't be found, terminate communication */
case p7_error_nocalc:
tries = 0;
break;
/* if it is a timeout, remove a try */
case p7_error_timeout:
tries--;
/* send a check */
err = p7_send_check(handle);
if (err) return (err);
/* set things */
wasresend = 0;
err = p7_error_timeout;
break;
/* if it is a bad checksum, ask for resend ; also,
* reset tries number in case of bad checksum between timeouts */
case p7_error_checksum:
/* if we shouldn't bother sending resend error, just
* transmit checksum error */
if (!checksum) break;
/* if we receive an invalid checksum while we are in packet
* shifting mode, or if we received an error when we sent a
* resend error, cut connexion... we can't do anything. */
if ((handle->_flags & p7_intflag_shifted) || wasresend)
return (p7_error_irrecoverable);
/* otherwise, send resend error */
err = p7_send_err_resend(handle);
if (err) return (err);
/* set things */
wasresend = 1;
err = p7_error_checksum;
break;
/* should not catch bad type & others */
default:
return (p7_error_unknown);
}
} while (err && tries);
/* return the error */
return (err);
}