rockbox/firmware/common/structec.c

194 lines
5.2 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 by Miika Pekkarinen
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <ctype.h>
#include <string.h>
#include <inttypes.h>
#include "structec.h"
#include "system.h"
#include "file.h"
#define MAX_STRUCT_SIZE 128
/**
* Convert the struct endianess with the instructions provided.
*
* For example:
* struct test {
* long par1;
* short par2;
* short par3;
* };
*
* structec_convert(instance_of_test, "lss", sizeof(struct test), true);
*
* Structures to be converted must be properly padded.
*
* @param structure Pointer to the struct being converted.
* @param ecinst Instructions how to do the endianess conversion.
* @param count Number of structures to write
* @param enable Conversion is not made unless this is true.
*/
void structec_convert(void *structure, const char *ecinst,
long count, bool enable)
{
const char *ecinst_ring = ecinst;
char *buf = (char *)structure;
if (!enable)
return;
while (count > 0)
{
switch (*ecinst_ring)
{
/* Swap nothing. */
case 'c':
{
buf++;
break;
}
/* Swap 2 bytes. */
case 's':
{
uint16_t *data = (uint16_t *)buf;
*data = swap16(*data);
buf += 2;
break;
}
/* Swap 4 bytes. */
case 'l':
{
uint32_t *data = (uint32_t *)buf;
*data = swap32(*data);
buf += 4;
break;
}
/* Skip N bytes, idea taken from metadata.c */
default:
{
if (isdigit(*ecinst_ring))
buf += (*ecinst_ring - '0');
break;
}
}
ecinst_ring++;
if (*ecinst_ring == '\0')
{
ecinst_ring = ecinst;
count--;
}
}
}
/**
* Determines the size of a struct in bytes by using endianess correction
* string format.
*
* @param ecinst endianess correction string.
* @return length of the struct in bytes.
*/
static size_t structec_size(const char *ecinst)
{
size_t size = 0;
do
{
switch (*ecinst)
{
case 'c': size += 1; break;
case 's': size += 2; break;
case 'l': size += 4; break;
default:
if (isdigit(*ecinst))
size += (*ecinst - '0');
}
} while (*(++ecinst) != '\0');
return size;
}
/**
* Reads endianess corrected structure members from the given file.
*
* @param fd file descriptor of the file being read.
* @param buf endianess corrected data is placed here.
* @param scount the number of struct members to read.
* @param ecinst endianess correction string.
* @param ec if true, endianess correction is enabled.
*/
ssize_t ecread(int fd, void *buf, size_t scount, const char *ecinst, bool ec)
{
ssize_t ret;
size_t member_size = structec_size(ecinst);
ret = read(fd, buf, scount * member_size);
structec_convert(buf, ecinst, scount, ec);
return ret;
}
/**
* Writes endianess corrected structure members to the given file.
*
* @param fd file descriptor of the file being written to.
* @param buf endianess corrected data is read here.
* @param scount the number of struct members to write.
* @param ecinst endianess correction string.
* @param ec if true, endianess correction is enabled.
*/
ssize_t ecwrite(int fd, const void *buf, size_t scount,
const char *ecinst, bool ec)
{
char tmp[MAX_STRUCT_SIZE];
ssize_t member_size = structec_size(ecinst);
if (ec)
{
const char *p = (const char *)buf;
int maxamount = (int)(MAX_STRUCT_SIZE / member_size);
int i;
for (i = 0; i < (long)scount; i += maxamount)
{
long amount = MIN((int)scount-i, maxamount);
memcpy(tmp, p, member_size * amount);
structec_convert(tmp, ecinst, amount, true);
ssize_t ret = write(fd, tmp, amount * member_size);
if(ret != amount * member_size)
return ret;
p += member_size * amount;
}
return scount * member_size;
}
return write(fd, buf, scount * member_size);
}