319 lines
5.7 KiB
C
319 lines
5.7 KiB
C
/****************************************************************************
|
|
* This file is part of the project AqHome.
|
|
* AqHome (c) by 2023 Martin Preuss, all rights reserved.
|
|
*
|
|
* The license for this file can be found in the file COPYING which you
|
|
* should have received along with this file.
|
|
****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
|
|
#include "aqhome/hexfile/hexfile_p.h"
|
|
|
|
#include <gwenhywfar/syncio.h>
|
|
#include <gwenhywfar/debug.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
static const char *_readRecord(AQH_HEXFILERECORD *hr, const char *s);
|
|
static void _writeRecord(AQH_HEXFILERECORD *hr, GWEN_BUFFER *buffer);
|
|
static int _sumValuesForChecksum(const AQH_HEXFILERECORD *hr);
|
|
static int _readWord(const char *s); /* big endian */
|
|
static int _readByte(const char *s);
|
|
static int _readNibble(const char *s);
|
|
|
|
|
|
|
|
|
|
|
|
AQH_HEXFILE *AQH_Hexfile_new()
|
|
{
|
|
AQH_HEXFILE *h;
|
|
|
|
GWEN_NEW_OBJECT(AQH_HEXFILE, h);
|
|
h->recordList=AQH_HexfileRecord_List_new();
|
|
return h;
|
|
}
|
|
|
|
|
|
|
|
AQH_HEXFILE *AQH_Hexfile_fromString(const char *s)
|
|
{
|
|
AQH_HEXFILE *h;
|
|
AQH_HEXFILERECORD_LIST *recordList;
|
|
|
|
h=AQH_Hexfile_new();
|
|
recordList=AQH_Hexfile_GetRecordList(h);
|
|
while(s && *s) {
|
|
AQH_HEXFILERECORD *hr;
|
|
uint8_t rt;
|
|
|
|
while(s && *s && *s!=':')
|
|
s++;
|
|
hr=AQH_HexfileRecord_new();
|
|
s=_readRecord(hr, s);
|
|
if (s==NULL) {
|
|
AQH_HexfileRecord_free(hr);
|
|
AQH_Hexfile_free(h);
|
|
return NULL;
|
|
}
|
|
rt=AQH_HexfileRecord_GetRecordType(hr);
|
|
DBG_INFO(AQH_LOGDOMAIN, "Adding record %02x", rt);
|
|
AQH_HexfileRecord_List_Add(hr, recordList);
|
|
if ((rt==AQH_HEXFILERECORD_TYPE_EOF) ||
|
|
(rt==AQH_HEXFILERECORD_TYPE_DATA && AQH_HexfileRecord_GetByteCount(hr)==0)) {
|
|
DBG_INFO(AQH_LOGDOMAIN, "End record found");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
|
|
|
|
AQH_HEXFILE *AQH_Hexfile_fromFile(const char *filename)
|
|
{
|
|
GWEN_BUFFER *buffer;
|
|
int rv;
|
|
AQH_HEXFILE *h;
|
|
|
|
buffer=GWEN_Buffer_new(0, 1024, 0, 1);
|
|
rv=GWEN_SyncIo_Helper_ReadFile(filename, buffer);
|
|
if (rv<0) {
|
|
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
|
|
GWEN_Buffer_free(buffer);
|
|
return NULL;
|
|
}
|
|
|
|
h=AQH_Hexfile_fromString(GWEN_Buffer_GetStart(buffer));
|
|
GWEN_Buffer_free(buffer);
|
|
if (h==NULL) {
|
|
DBG_INFO(AQH_LOGDOMAIN, "here");
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
|
|
|
|
void AQH_Hexfile_toBuffer(const AQH_HEXFILE *h, GWEN_BUFFER *buffer)
|
|
{
|
|
if (h && h->recordList) {
|
|
AQH_HEXFILERECORD *hr;
|
|
|
|
hr=AQH_HexfileRecord_List_First(h->recordList);
|
|
while(hr) {
|
|
_writeRecord(hr, buffer);
|
|
GWEN_Buffer_AppendString(buffer, "\r\n");
|
|
hr=AQH_HexfileRecord_List_Next(hr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void AQH_Hexfile_free(AQH_HEXFILE *h)
|
|
{
|
|
if (h) {
|
|
AQH_HexfileRecord_List_free(h->recordList);
|
|
GWEN_FREE_OBJECT(h);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
AQH_HEXFILERECORD_LIST *AQH_Hexfile_GetRecordList(const AQH_HEXFILE *h)
|
|
{
|
|
if (h)
|
|
return h->recordList;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char *_readRecord(AQH_HEXFILERECORD *hr, const char *s)
|
|
{
|
|
int v;
|
|
int dataLen;
|
|
|
|
/* find ":" */
|
|
s=strchr(s, ':');
|
|
if (s==NULL)
|
|
return NULL;
|
|
s++;
|
|
|
|
v=_readByte(s);
|
|
if (v<0)
|
|
return NULL;
|
|
AQH_HexfileRecord_SetByteCount(hr, (uint8_t) v);
|
|
dataLen=v;
|
|
s+=2;
|
|
|
|
v=_readWord(s);
|
|
if (v<0)
|
|
return NULL;
|
|
AQH_HexfileRecord_SetAddress(hr, (uint16_t) v);
|
|
s+=4;
|
|
|
|
v=_readByte(s);
|
|
if (v<0)
|
|
return NULL;
|
|
AQH_HexfileRecord_SetRecordType(hr, (uint8_t) v);
|
|
s+=2;
|
|
|
|
if (dataLen) {
|
|
uint8_t *ptr;
|
|
int i;
|
|
|
|
ptr=(uint8_t*) malloc(dataLen);
|
|
if (ptr==NULL) {
|
|
return NULL;
|
|
}
|
|
AQH_HexfileRecord_SetDataPointer(hr, ptr);
|
|
|
|
for (i=0; i<dataLen; i++) {
|
|
v=_readByte(s);
|
|
if (v<0)
|
|
return NULL;
|
|
ptr[i]=(uint8_t) v;
|
|
s+=2;
|
|
}
|
|
}
|
|
|
|
/* checksum */
|
|
v=_readByte(s);
|
|
if (v<0)
|
|
return NULL;
|
|
s+=2;
|
|
|
|
v+=_sumValuesForChecksum(hr);
|
|
if (v & 0xff) {
|
|
DBG_ERROR(AQH_LOGDOMAIN, "Invalid checksum (%d, %d)", (v & 0xff), -(v & 0xff));
|
|
return NULL;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
|
|
void _writeRecord(AQH_HEXFILERECORD *hr, GWEN_BUFFER *buffer)
|
|
{
|
|
uint8_t len;
|
|
int v;
|
|
|
|
len=AQH_HexfileRecord_GetByteCount(hr);
|
|
GWEN_Buffer_AppendArgs(buffer, ":%02X%04X%02X",
|
|
AQH_HexfileRecord_GetByteCount(hr),
|
|
AQH_HexfileRecord_GetAddress(hr),
|
|
AQH_HexfileRecord_GetRecordType(hr));
|
|
if (len) {
|
|
const uint8_t *ptr;
|
|
int i;
|
|
|
|
ptr=AQH_HexfileRecord_GetDataPointer(hr);
|
|
for(i=0; i<len; i++)
|
|
GWEN_Buffer_AppendArgs(buffer, "%02X", ptr[i]);
|
|
}
|
|
|
|
v=_sumValuesForChecksum(hr);
|
|
GWEN_Buffer_AppendArgs(buffer, "%02X", (-v) & 0xff);
|
|
}
|
|
|
|
|
|
|
|
int _sumValuesForChecksum(const AQH_HEXFILERECORD *hr)
|
|
{
|
|
int v;
|
|
int dataLen;
|
|
|
|
dataLen=AQH_HexfileRecord_GetByteCount(hr);
|
|
v=AQH_HexfileRecord_GetByteCount(hr)+
|
|
((AQH_HexfileRecord_GetAddress(hr)>>8) & 0xff)+(AQH_HexfileRecord_GetAddress(hr) & 0xff)+
|
|
AQH_HexfileRecord_GetRecordType(hr);
|
|
if (dataLen) {
|
|
const uint8_t *ptr;
|
|
int i;
|
|
|
|
ptr=AQH_HexfileRecord_GetDataPointer(hr);
|
|
for(i=0; i<dataLen; i++)
|
|
v+=ptr[i];
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
|
|
|
|
|
|
int _readWord(const char *s)
|
|
{
|
|
uint16_t result;
|
|
int b;
|
|
|
|
b=_readByte(s);
|
|
if (b<0)
|
|
return -1;
|
|
result=(uint16_t)(b & 0xff);
|
|
s+=2;
|
|
|
|
b=_readByte(s++);
|
|
if (b<0)
|
|
return -1;
|
|
result<<=8;
|
|
result|=(uint16_t)(b & 0xff);
|
|
return (int) result;
|
|
}
|
|
|
|
|
|
|
|
int _readByte(const char *s)
|
|
{
|
|
uint8_t result;
|
|
int c;
|
|
|
|
c=_readNibble(s++);
|
|
if (c<0)
|
|
return -1;
|
|
result=(uint8_t) (c & 0xf);
|
|
c=_readNibble(s);
|
|
if (c<0)
|
|
return -1;
|
|
result<<=4;
|
|
result|=(uint8_t) (c & 0xf);
|
|
return (int) result;
|
|
}
|
|
|
|
|
|
|
|
int _readNibble(const char *s)
|
|
{
|
|
uint8_t c;
|
|
|
|
c=*s;
|
|
if (c==0)
|
|
return -1;
|
|
if (c>='0' && c<='9')
|
|
c-='0';
|
|
else if (c>='A' && c<='F')
|
|
c-='A'-10;
|
|
else if (c>='a' && c<='f')
|
|
c-='a'-10;
|
|
else
|
|
return -1;
|
|
return c;
|
|
}
|
|
|
|
|
|
|