forked from len0rd/rockbox
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7039 a1c6a512-1295-4272-9138-f99709370657
450 lines
11 KiB
C
450 lines
11 KiB
C
#include "malloc.h" // realloc() and free()
|
|
#include <string.h> // strncasecmp()
|
|
|
|
#include "song.h"
|
|
|
|
// how is our flag organized?
|
|
#define FLAG ( 0xCF )
|
|
#define FLAG_VALID(flag) (flag == 0xCF)
|
|
|
|
static int do_resize(struct song_entry *e, const uint32_t name_len, const uint32_t genre_len, const int zero_fill);
|
|
|
|
struct song_entry* new_song_entry(const uint32_t name_len, const uint32_t genre_len) {
|
|
// Start my allocating memory
|
|
struct song_entry *e = (struct song_entry*)malloc(sizeof(struct song_entry));
|
|
if( e == NULL ) {
|
|
DEBUGF("new_song_entry: could not allocate memory\n");
|
|
return NULL;
|
|
}
|
|
|
|
// We begin empty
|
|
e->name = NULL;
|
|
e->size.name_len = 0;
|
|
|
|
e->artist = 0;
|
|
e->album = 0;
|
|
e->file = 0;
|
|
|
|
e->genre = NULL;
|
|
e->size.genre_len = 0;
|
|
|
|
e->bitrate = 0;
|
|
e->year = 0;
|
|
e->playtime = 0;
|
|
e->track = 0;
|
|
e->samplerate = 0;
|
|
|
|
e->flag = FLAG;
|
|
|
|
// and resize to the requested size
|
|
if( do_resize(e, name_len, genre_len, 1) ) {
|
|
free(e);
|
|
return NULL;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
int song_entry_destruct(struct song_entry *e) {
|
|
assert(e != NULL);
|
|
assert(FLAG_VALID(e->flag));
|
|
|
|
free(e->name);
|
|
free(e->genre);
|
|
|
|
free(e);
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
static int do_resize(struct song_entry *e, const uint32_t name_len, const uint32_t genre_len, const int zero_fill) {
|
|
void* temp;
|
|
|
|
assert(e != NULL);
|
|
assert(FLAG_VALID(e->flag));
|
|
|
|
// begin with name
|
|
if( name_len != e->size.name_len ) {
|
|
temp = realloc(e->name, name_len);
|
|
if(temp == NULL && name_len > 0) { // if realloc(,0) don't complain about NULL-pointer
|
|
DEBUGF("song_entry_resize: out of memory to resize name\n");
|
|
return ERR_MALLOC;
|
|
}
|
|
e->name = (char*)temp;
|
|
|
|
// if asked, fill it with zero's
|
|
if( zero_fill ) {
|
|
uint32_t i;
|
|
for(i=e->size.name_len; i<name_len; i++)
|
|
e->name[i] = (char)0x00;
|
|
}
|
|
|
|
e->size.name_len = name_len;
|
|
}
|
|
|
|
// now the genre
|
|
if( genre_len != e->size.genre_len ) {
|
|
temp = realloc(e->genre, genre_len);
|
|
if(temp == NULL && genre_len > 0) { // if realloc(,0) don't complain about NULL-pointer
|
|
DEBUGF("song_entry_resize: out of memory to resize genre\n");
|
|
return ERR_MALLOC;
|
|
}
|
|
e->genre = (char*)temp;
|
|
|
|
// if asked, fill it with zero's
|
|
if( zero_fill ) {
|
|
uint32_t i;
|
|
for(i=e->size.genre_len; i<genre_len; i++)
|
|
e->genre[i] = (char)0x00;
|
|
}
|
|
|
|
e->size.genre_len = genre_len;
|
|
}
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
inline int song_entry_resize(struct song_entry *e, const uint32_t name_len, const uint32_t genre_len) {
|
|
return do_resize(e, name_len, genre_len, 1);
|
|
}
|
|
|
|
int song_entry_serialize(FILE *fd, const struct song_entry *e) {
|
|
assert(fd != NULL);
|
|
assert(e != NULL);
|
|
assert(FLAG_VALID(e->flag));
|
|
|
|
// First byte we write is a flag-byte to indicate this is a valid record
|
|
if( fwrite(&e->flag, 1, 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_serialize: failed to write flag-byte\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Write the length of the name field
|
|
if( fwrite(&e->size.name_len, sizeof(e->size.name_len), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_serialize: failed to write name_len\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// now the name field itself
|
|
if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) {
|
|
DEBUGF("song_entry_serialize: failed to write name\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Artist field
|
|
if( fwrite(&e->artist, sizeof(e->artist), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_serialize: failed to write artist\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Album field
|
|
if( fwrite(&e->album, sizeof(e->album), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_serialize: failed to write album\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// File field
|
|
if( fwrite(&e->file, sizeof(e->file), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_serialize: failed to write file\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// length of genre field
|
|
if( fwrite(&e->size.genre_len, sizeof(e->size.genre_len), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_serialize: failed to write genre_len\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// genre itself
|
|
if( fwrite(e->genre, 1, e->size.genre_len, fd) != e->size.genre_len ) {
|
|
DEBUGF("song_entry_serialize: failed to write genre\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Bitrate field
|
|
if( fwrite(&e->bitrate, sizeof(e->bitrate), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_serialize: failed to write bitrate\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Year field
|
|
if( fwrite(&e->year, sizeof(e->year), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_serialize: failed to write year\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Playtime field
|
|
if( fwrite(&e->playtime, sizeof(e->playtime), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_serialize: failed to write playtime\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Track field
|
|
if( fwrite(&e->track, sizeof(e->track), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_serialize: failed to write track\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Samplerate field
|
|
if( fwrite(&e->samplerate, sizeof(e->samplerate), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_serialize: failed to write samplerate\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
int song_entry_unserialize(struct song_entry **dest, FILE *fd) {
|
|
uint32_t length;
|
|
struct song_entry* e;
|
|
|
|
assert(dest != NULL);
|
|
assert(fd != NULL);
|
|
|
|
// Allocate memory
|
|
e = new_song_entry(0, 0);
|
|
if( e == NULL ) {
|
|
DEBUGF("song_entry_unserialize: could not create new song_entry\n");
|
|
return ERR_MALLOC;
|
|
}
|
|
|
|
// First we read the length of the name field
|
|
if( fread(&length, sizeof(length), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_unserialize: failed to read name_len\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// allocate memory for the upcomming name-field
|
|
if( do_resize(e, length, 0, 0) ) {
|
|
DEBUGF("song_entry_unserialize: failed to allocate memory for name\n");
|
|
song_entry_destruct(e);
|
|
return ERR_MALLOC;
|
|
}
|
|
|
|
// read it in
|
|
if( fread(e->name, 1, e->size.name_len, fd) != e->size.name_len ) {
|
|
DEBUGF("song_entry_unserialize: failed to read name\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Artist field
|
|
if( fread(&e->artist, sizeof(e->artist), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_unserialize: failed to read artist\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Album field
|
|
if( fread(&e->album, sizeof(e->album), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_unserialize: failed to read album\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// File field
|
|
if( fread(&e->file, sizeof(e->file), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_unserialize: failed to read file\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Next the length of genre
|
|
if( fread(&length, sizeof(length), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_unserialize: failed to read genre_len\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// allocate memory for the upcomming name-field
|
|
if( do_resize(e, e->size.name_len, length, 0) ) {
|
|
DEBUGF("song_entry_unserialize: failed to allocate memory for song\n");
|
|
song_entry_destruct(e);
|
|
return ERR_MALLOC;
|
|
}
|
|
|
|
// read it in
|
|
if( fread(e->genre, 1, e->size.genre_len, fd) != e->size.genre_len ) {
|
|
DEBUGF("song_entry_unserialize: failed to read genre\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Bitrate field
|
|
if( fread(&e->bitrate, sizeof(e->bitrate), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_unserialize: failed to read bitrate\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Year field
|
|
if( fread(&e->year, sizeof(e->year), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_unserialize: failed to read year\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Playtime field
|
|
if( fread(&e->playtime, sizeof(e->playtime), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_unserialize: failed to read playtime\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Track field
|
|
if( fread(&e->track, sizeof(e->track), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_unserialize: failed to read track\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// Samplerate field
|
|
if( fread(&e->samplerate, sizeof(e->samplerate), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_unserialize: failed to read samplerate\n");
|
|
song_entry_destruct(e);
|
|
return ERR_FILE;
|
|
}
|
|
|
|
*dest = e;
|
|
return ERR_NONE;
|
|
}
|
|
|
|
int song_entry_write(FILE *fd, struct song_entry *e, struct song_size *s) {
|
|
uint32_t be32;
|
|
uint16_t be16;
|
|
char pad = 0x00;
|
|
|
|
assert(fd != NULL);
|
|
assert(e != NULL);
|
|
assert(FLAG_VALID(e->flag));
|
|
|
|
// song name
|
|
if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) {
|
|
DEBUGF("song_entry_write: failed to write name\n");
|
|
return ERR_FILE;
|
|
}
|
|
// pad the rest (abuse be32 for counter)
|
|
be32 = e->size.name_len;
|
|
while( s != NULL && s->name_len > be32) {
|
|
if( fwrite(&pad, 1, 1, fd) == 1 ) {
|
|
be32++;
|
|
} else {
|
|
DEBUGF("genre_entry_write: failed to pad name\n");
|
|
return ERR_FILE;
|
|
}
|
|
}
|
|
|
|
// artist
|
|
be32 = BE32(e->artist);
|
|
if( fwrite(&be32, sizeof(be32), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_write: failed to write artist\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// album
|
|
be32 = BE32(e->album);
|
|
if( fwrite(&be32, sizeof(be32), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_write: failed to write album\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// file
|
|
be32 = BE32(e->file);
|
|
if( fwrite(&be32, sizeof(be32), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_write: failed to write file\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// genre
|
|
if( fwrite(e->genre, 1, e->size.genre_len, fd) != e->size.genre_len ) {
|
|
DEBUGF("song_entry_write: failed to write genre\n");
|
|
return ERR_FILE;
|
|
}
|
|
// pad the rest (abuse be32 for counter)
|
|
be32 = e->size.genre_len;
|
|
while( s != NULL && s->genre_len > be32) {
|
|
if( fwrite(&pad, 1, 1, fd) == 1 ) {
|
|
be32++;
|
|
} else {
|
|
DEBUGF("genre_entry_write: failed to pad genre\n");
|
|
return ERR_FILE;
|
|
}
|
|
}
|
|
|
|
// bitrate
|
|
be16 = BE16(e->bitrate);
|
|
if( fwrite(&be16, sizeof(be16), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_write: failed to write bitrate\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// year
|
|
be16 = BE16(e->year);
|
|
if( fwrite(&be16, sizeof(be16), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_write: failed to write year\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// playtime
|
|
be32 = BE32(e->playtime);
|
|
if( fwrite(&be32, sizeof(be32), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_write: failed to write playtime\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// track
|
|
be16 = BE16(e->track);
|
|
if( fwrite(&be16, sizeof(be16), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_write: failed to write track\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
// samplerate
|
|
be16 = BE16(e->samplerate);
|
|
if( fwrite(&be16, sizeof(be16), 1, fd) != 1 ) {
|
|
DEBUGF("song_entry_write: failed to write samplerate\n");
|
|
return ERR_FILE;
|
|
}
|
|
|
|
return ERR_NONE;
|
|
}
|
|
|
|
inline int song_entry_compare(const struct song_entry *a, const struct song_entry *b) {
|
|
assert(a != NULL);
|
|
assert(b != NULL);
|
|
return strncasecmp(a->name, b->name, (a->size.name_len <= b->size.name_len ? a->size.name_len : b->size.name_len) );
|
|
}
|
|
|
|
struct song_size* new_song_size() {
|
|
struct song_size *s;
|
|
s = (struct song_size*)malloc(sizeof(struct song_size));
|
|
if( s == NULL ) {
|
|
DEBUGF("new_song_size: failed to allocate memory\n");
|
|
return NULL;
|
|
}
|
|
s->name_len = 0;
|
|
s->genre_len = 0;
|
|
|
|
return s;
|
|
}
|
|
|
|
inline uint32_t song_size_get_length(const struct song_size *size) {
|
|
assert(size != NULL);
|
|
return size->name_len + size->genre_len + 6*4;
|
|
}
|
|
|
|
inline int song_size_max(struct song_size *s, const struct song_entry *e) {
|
|
assert(s != NULL);
|
|
assert(e != NULL);
|
|
assert(FLAG_VALID(e->flag));
|
|
s->name_len = ( s->name_len >= e->size.name_len ? s->name_len : e->size.name_len );
|
|
s->genre_len = ( s->genre_len >= e->size.genre_len ? s->genre_len : e->size.genre_len );
|
|
return ERR_NONE;
|
|
}
|
|
|
|
int song_size_destruct(struct song_size *s) {
|
|
assert(s != NULL);
|
|
// nothing to do...
|
|
free(s);
|
|
return ERR_NONE;
|
|
}
|