/*

  Zerocat Chipflasher --- Flash free firmware, kick the Management Engine.

  Copyright (C) 2017, 2018, 2020, 2021, 2022, 2026  Kai Mertens <kmx@posteo.net>

  This file is part of Zerocat Chipflasher.

  Zerocat Chipflasher 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.

  Zerocat Chipflasher 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 Zerocat Chipflasher.
  If not, see <http://www.gnu.org/licenses/>.


***/


# include "./lineinfo.h"

# ifndef __LINESPEC_H__
#   include "../../firmware1/src/libcommon/linespec.h"
# endif


enum CONNECT_LINEFIELD_t lineinfo (
  char linetype,                            // Characterizes the type of the line being processed, i.e. HEXD or SREC.
  int * piline,                             // The current byte position in the line beeing processed, one-based.
  char c,                                   // The value of the processed character.
  struct CONNECT_LINEHEXD_t * plineHEXD,    // To be used in case a Hex-Dump line is to be processed.
  struct CONNECT_LINESREC_t * plineSREC,    // To be used in case a Motorola-S line is to be processed.
  int hexmode                               // If zero, binary payload data is used with Motorola-S.
)
{
  /*

    Helps to determine the role of a byte within the line being processed.

    member *piline:

      - *piline == SCANNER_OFF:  This function has no effect, return value is zero.
      - *piline == SCANNER_INI:  Struct initialization
      - *piline == SCAN_S, SCAN_START:   Points to first byte of line.

    Returns IS_LINE per default,
    otherwise IS_ADDRESS or IS_PAYLOAD.

  ***/


  enum CONNECT_LINEFIELD_t linefield = IS_LINE;

  switch(*piline) {
    case SCANNER_OFF:
      break;

    case SCANNER_INI:
      // clear structs
      if(plineSREC != NULL) {
        memset(plineSREC, 0, sizeof(struct CONNECT_LINESREC_t));
      }
      if(plineHEXD != NULL) {
        plineHEXD->is_print = OFF;
        plineHEXD->is_info = OFF;
        plineHEXD->is_first = '-';
      }

    default:    // SCANNER_ON
      *piline += 1;
      switch(linetype) {
        case LINETYPE_SREC:
          switch(*piline) {
            case SCAN_S:              // S
              break;

            case SCAN_STYPE:          // S Type
              plineSREC->sizeofaddr = SREC_addrlen(c);
              plineSREC->pos_addrlsb = 3 + (plineSREC->sizeofaddr << 1);
              break;

            case SCAN_RECLEN_MSB:     // Record Length MSB
              plineSREC->reclen = hexdigit2bin(c) << 4;
              break;

            case SCAN_RECLEN_LSB:     // Record Length LSB
              plineSREC->reclen |= hexdigit2bin(c);
              plineSREC->checksum = plineSREC->reclen;    // start summing checksum
              plineSREC->ndata = (plineSREC->reclen - plineSREC->sizeofaddr - 1) << (hexmode ? 1 : 0);
              plineSREC->size = plineSREC->reclen + plineSREC->sizeofaddr + 8;
              break;

            default:
              if(*piline <= plineSREC->pos_addrlsb) {     // process address
                linefield = IS_ADDRESS;
                plineSREC->checksum += (*piline% 2) ? hexdigit2bin(c) : hexdigit2bin(c) << 4 ;
                if(*piline == plineSREC->pos_addrlsb)
                  plineSREC->payload = plineSREC->ndata;
              }
              else if(plineSREC->payload) {               // process payload
                linefield = IS_PAYLOAD;
                plineSREC->checksum += c;
                plineSREC->payload--;
              }
              else {
                if(*piline == plineSREC->pos_addrlsb + plineSREC->ndata + 1) {     // checksum rx MSB
                  plineSREC->chksum_rx = hexdigit2bin(c) << 4;
                }
                else if(*piline == plineSREC->pos_addrlsb + plineSREC->ndata + 2) {     // checksum rx LSB
                  plineSREC->chksum_rx |= hexdigit2bin(c);
                  plineSREC->checksum = ~plineSREC->checksum;
                }
              }
              break;
          }
          break;

        case LINETYPE_HEXD:
          if(*piline == SCAN_START)
            plineHEXD->is_first = c;
          if(c == '#') {
            plineHEXD->is_print = ON;
            if(*piline == SCAN_START)
              plineHEXD->is_info = ON;
          }
          break;
      }
      break;
  }
  return linefield;
}
