From: <suc...@us...> - 2007-02-13 21:22:22
|
Revision: 359 http://svn.sourceforge.net/pmplib/?rev=359&view=rev Author: sucknblow Date: 2007-02-13 13:22:10 -0800 (Tue, 13 Feb 2007) Log Message: ----------- svn:ignore some generated build system files. svn:eol-style native the pmp_iriverplus3 files, (except the .vcproj file) which seem to have ^M characters. Modified Paths: -------------- trunk/pmplib/lib/pmp_iriverplus3/dat.c trunk/pmplib/lib/pmp_iriverplus3/dat.h trunk/pmplib/lib/pmp_iriverplus3/dic.c trunk/pmplib/lib/pmp_iriverplus3/dic.h trunk/pmplib/lib/pmp_iriverplus3/idx.c trunk/pmplib/lib/pmp_iriverplus3/idx.h trunk/pmplib/lib/pmp_iriverplus3/ip3db.c trunk/pmplib/lib/pmp_iriverplus3/ip3db.h trunk/pmplib/lib/pmp_iriverplus3/pmp_iriverplus3.c trunk/pmplib/lib/pmp_iriverplus3/serialize.c trunk/pmplib/lib/pmp_iriverplus3/serialize.h trunk/pmplib/lib/pmp_iriverplus3/util.c trunk/pmplib/lib/pmp_iriverplus3/util.h Property Changed: ---------------- trunk/pmplib/ trunk/pmplib/include/ trunk/pmplib/include/pmplib/ trunk/pmplib/lib/pmp_iriverplus3/ trunk/pmplib/lib/pmp_iriverplus3/Makefile.am trunk/pmplib/lib/pmp_iriverplus3/dat.c trunk/pmplib/lib/pmp_iriverplus3/dat.h trunk/pmplib/lib/pmp_iriverplus3/dic.c trunk/pmplib/lib/pmp_iriverplus3/dic.h trunk/pmplib/lib/pmp_iriverplus3/idx.c trunk/pmplib/lib/pmp_iriverplus3/idx.h trunk/pmplib/lib/pmp_iriverplus3/ip3db.c trunk/pmplib/lib/pmp_iriverplus3/ip3db.h trunk/pmplib/lib/pmp_iriverplus3/pmp_iriverplus3.c trunk/pmplib/lib/pmp_iriverplus3/serialize.c trunk/pmplib/lib/pmp_iriverplus3/serialize.h trunk/pmplib/lib/pmp_iriverplus3/util.c trunk/pmplib/lib/pmp_iriverplus3/util.h trunk/pmplib/m4/ Property changes on: trunk/pmplib ___________________________________________________________________ Name: svn:ignore + Makefile.in Makefile Property changes on: trunk/pmplib/include ___________________________________________________________________ Name: svn:ignore + Makefile.in Makefile Property changes on: trunk/pmplib/include/pmplib ___________________________________________________________________ Name: svn:ignore + Makefile.in Makefile Property changes on: trunk/pmplib/lib/pmp_iriverplus3 ___________________________________________________________________ Name: svn:ignore + Makefile.in Makefile .libs .deps Property changes on: trunk/pmplib/lib/pmp_iriverplus3/Makefile.am ___________________________________________________________________ Name: svn:eol-style + native Modified: trunk/pmplib/lib/pmp_iriverplus3/dat.c =================================================================== --- trunk/pmplib/lib/pmp_iriverplus3/dat.c 2007-02-12 21:14:08 UTC (rev 358) +++ trunk/pmplib/lib/pmp_iriverplus3/dat.c 2007-02-13 21:22:10 UTC (rev 359) @@ -1,866 +1,866 @@ -/* - * Low-level library for db.dat. - * - * Copyright (c) 2005-2007 Naoaki Okazaki - * - * This library 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 2.1 of the License, or (at your option) any later version. - * - * This library 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 this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -/* $Id$ */ - -/* -Brief summary of db.dat structure: -- 0x00000000-0x0001FFFF: object (path name) chunk -- 0x00020000-0x0003FFFF: music (media information) chunk -- Each chunk has a 16-bytes header at the beginning -- Each chunk has an array of offsets to actual entries at the end (backward) -- Field names in db.dat are defined in db.dic (Music and Objects) -*/ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif/*HAVE_CONFIG_H*/ - -#include <os.h> -#include <stdio.h> -#include <stdlib.h> -#include <memory.h> -#include <pmplib/ucs2char.h> - -#include "serialize.h" -#include "util.h" -#include "ip3db.h" -#include "dic.h" -#include "dat.h" - -#define PAGESIZE 0x00020000 -#define COMP(a, b) ((a)>(b))-((a)<(b)) - -typedef struct { - uint32_t size; - uint32_t num_entries; - uint32_t unknown1; - uint32_t next_page; -} page_header_t; - -struct tag_sort_index_t { - const void* base; - int index; -}; - -static void dat_entry_init(dat_entry_t* entry, const dic_table_t* dic_list) -{ - memset(entry, 0, sizeof(*entry)); - entry->fields = (ip3db_variant_t*)malloc(sizeof(ip3db_variant_t) * dic_list->num_fields); - if (entry->fields) { - int i; - entry->num_fields = dic_list->num_fields; - for (i = 0;i < entry->num_fields;++i) { - ip3db_variant_init(&entry->fields[i], dic_list->fields[i].type); - } - } -} - -static void dat_entry_finish(dat_entry_t* entry) -{ - if (entry) { - int i; - for (i = 0;i < entry->num_fields;++i) { - ip3db_variant_finish(&entry->fields[i]); - } - free(entry->fields); - memset(entry, 0, sizeof(*entry)); - } -} - -static size_t dat_entry_size(dat_entry_t* entry) -{ - int i; - size_t size = 0; - for (i = 0;i < entry->num_fields;++i) { - ip3db_variant_t* var = &entry->fields[i]; - switch (var->type) { - case IP3DBVT_STRING: - size += var->value.str ? sizeof(ucs2char_t) * (ucs2len(var->value.str) + 1) : sizeof(ucs2char_t); - break; - case IP3DBVT_BYTE: - size += sizeof(uint8_t); - break; - case IP3DBVT_WORD: - size += sizeof(uint16_t); - break; - case IP3DBVT_DWORD: - size += sizeof(uint32_t); - break; - } - } - return size; -} - -static size_t dat_entry_serialize(dat_entry_t* entry, uint8_t* block, int is_storing) -{ - int i; - uint8_t *p = block; - - /* Serialize all fields in this entry. */ - for (i = 0;i < entry->num_fields;++i) { - ip3db_variant_t* var = &entry->fields[i]; - switch (var->type) { - case IP3DBVT_STRING: - if (is_storing) { - if (var->value.str) { - p += (serialize_ucs2be_string_var(p, var->value.str, is_storing) + 1) * sizeof(ucs2char_t); - } else { - ucs2char_t v = 0; - p += serialize_ucs2be(p, &v, is_storing); - } - } else { - p += (serialize_ucs2be_string_var_alloc(p, &var->value.str) + 1) * sizeof(ucs2char_t); - } - break; - case IP3DBVT_BYTE: - p += serialize_uint8(p, &var->value.byte, is_storing); - break; - case IP3DBVT_WORD: - p += serialize_uint16be(p, &var->value.word, is_storing); - break; - case IP3DBVT_DWORD: - p += serialize_uint32be(p, &var->value.dword, is_storing); - break; - } - } - - return (size_t)(p - block); -} - -static void dat_entry_dump(dat_entry_t* entry, const dic_table_t* dic_list, FILE *fp) -{ - int i; - - /* Loop for all fields in this entry. */ - for (i = 0;i < entry->num_fields;++i) { - ip3db_variant_t* var = &entry->fields[i]; - - /* Output the field name. */ - fprints(fp, " %s: ", dic_list->fields[i].name); - - /* Output its value. */ - switch (var->type) { - case IP3DBVT_STRING: - fprints(fp, "%s\n", var->value.str); - break; - case IP3DBVT_BYTE: - fprintf(fp, "0x%02X\n", var->value.byte); - break; - case IP3DBVT_WORD: - fprintf(fp, "%d\n", var->value.word); - break; - case IP3DBVT_DWORD: - fprintf(fp, "%d\n", var->value.dword); - break; - } - } -} - - - -static void dat_list_init(dat_list_t* list) -{ - memset(list, 0, sizeof(*list)); -} - -static void dat_list_finish(dat_list_t* list) -{ - if (list) { - if (list->entries) { - uint32_t i; - for (i = 0;i < list->num_entries;++i) { - dat_entry_finish(&list->entries[i]); - } - free(list->entries); - } - memset(list, 0, sizeof(*list)); - } -} - -static dat_entry_t *dat_list_expand(dat_list_t* list) -{ - list->entries = (dat_entry_t*)realloc(list->entries, sizeof(dat_entry_t) * (list->num_entries+1)); - return &list->entries[list->num_entries++]; -} - -static size_t dat_list_read(dat_list_t* list, page_header_t* header, const dic_table_t* dic_list, uint8_t* buffer, uint32_t start) -{ - uint32_t i; - uint8_t *p = buffer + start; - uint8_t *q = buffer + start + PAGESIZE - sizeof(uint32_t); - - /* Read the header. */ - p += serialize_uint32be(p, &header->size, 0); - p += serialize_uint32be(p, &header->num_entries, 0); - p += serialize_uint32be(p, &header->unknown1, 0); - p += serialize_uint32be(p, &header->next_page, 0); - - /* Expand the array of records. */ - list->entries = (dat_entry_t*)realloc(list->entries, sizeof(dat_entry_t) * (list->num_entries + header->num_entries)); - for (i = 0;i < header->num_entries;++i) { - dat_entry_init(&list->entries[list->num_entries+i], dic_list); - } - - /* Read the new records. */ - for (i = 0;i < header->num_entries;++i) { - uint32_t offset = 0; - dat_entry_t* entry = &list->entries[list->num_entries+i]; - - /* Read the offset table. */ - q -= serialize_uint32be(q, &offset, 0); - entry->offset = offset + start; - - p += dat_entry_serialize(entry, buffer + entry->offset, 0); - } - - list->num_entries += header->num_entries; - return (size_t)(header->size); -} - -static size_t dat_list_write(dat_list_t* list, uint32_t i, page_header_t* header, uint8_t* buffer, uint32_t start) -{ - uint8_t *p = buffer + start; - uint8_t *q = buffer + start + PAGESIZE - sizeof(uint32_t); - - header->size = 0; - header->num_entries = 0; - - /* Skip the header for now when writing. */ - p += sizeof(uint32_t) * 4; - - /* Write records. */ - while (i < list->num_entries) { - uint32_t offset = 0; - size_t free_space = (size_t)(q-p); - dat_entry_t* entry = &list->entries[i]; - if (free_space < dat_entry_size(entry)) { - break; - } - entry->offset = (uint32_t)(p - buffer); /* compute the current offset address */ - - offset = entry->offset - start; - q -= serialize_uint32be(q, &offset, 1); - - p += dat_entry_serialize(entry, p, 1); - header->num_entries++; - i++; - } - - /* Compute the block size and write the header. */ - header->size = (uint32_t)(p - (buffer + start)); - if (list->num_entries <= i) { - header->next_page = 0; - } - - p = buffer + start; - p += serialize_uint32be(p, &header->size, 1); - p += serialize_uint32be(p, &header->num_entries, 1); - p += serialize_uint32be(p, &header->unknown1, 1); - p += serialize_uint32be(p, &header->next_page, 1); - return header->size; -} - -static void dat_list_dump(dat_list_t* list, const dic_table_t* dic_list, FILE *fp) -{ - uint32_t i; - for (i = 0;i < list->num_entries;++i) { - dat_entry_t* entry = &list->entries[i]; - fprintf(fp, " ENTRY %d (0x%08X) = {\n", i, entry->offset); - dat_entry_dump(entry, dic_list, fp); - fprintf(fp, " }\n"); - } -} - - - -static int comp_object_uid(const void *__x, const void *__y) -{ - const sort_index_t* _x = (const sort_index_t*)__x; - const sort_index_t* _y = (const sort_index_t*)__y; - const dat_list_t* _xb = (const dat_list_t*)_x->base; - const dat_list_t* _yb = (const dat_list_t*)_y->base; - const ip3db_variant_t* x = _xb->entries[_x->index].fields; - const ip3db_variant_t* y = _yb->entries[_y->index].fields; - return COMP(x[IP3DBF_OBJECTS_UID].value.dword, y[IP3DBF_OBJECTS_UID].value.dword); -} - -static sort_index_t* dat_uidmap_create(dat_list_t* list) -{ - int i; - sort_index_t* si = (sort_index_t*)malloc(sizeof(sort_index_t) * list->num_entries); - - if (si) { - /* Sort UIDs. */ - for (i = 0;i < list->num_entries;++i) { - si[i].base = list; - si[i].index = i; - } - qsort(si, list->num_entries, sizeof(si[0]), comp_object_uid); - } - return si; -} - -static void dat_uidmap_finish(sort_index_t* si) -{ - free(si); -} - -static int dat_uidmap_get(sort_index_t* si, dat_list_t* list, uint32_t uid) -{ - int low = 0, high = list->num_entries-1; - - /* Binary search. */ - while (low <= high) { - int middle = (low + high) / 2; - int comp = COMP(uid, list->entries[si[middle].index].fields[IP3DBF_OBJECTS_UID].value.dword); - if (comp == 0) { - /* Found */ - return si[middle].index; - } else if (comp < 0) { - high = middle - 1; - } else { - low = middle + 1; - } - } - return -1; -} - -static ucs2char_t* ucs2append(const ucs2char_t* x, const ucs2char_t* y) -{ - ucs2char_t* ret = NULL; - size_t length = 0; - length += x ? ucs2len(x) : 0; - length += y ? ucs2len(y) : 0; - ret = ucs2calloc(sizeof(ucs2char_t) * (length+1)); - if (x) ucs2cat(ret, x); - if (y) ucs2cat(ret, y); - return ret; -} - - - -dat_t* dat_new() -{ - dat_t* dat = (dat_t*)calloc(1, sizeof(dat_t)); - if (dat) { - dat_list_init(&dat->objects); - dat_list_init(&dat->musics); - dat_list_init(&dat->references); - } - return dat; -} - -void dat_finish(dat_t* dat) -{ - dat_list_finish(&dat->objects); - dat_list_finish(&dat->musics); - dat_list_finish(&dat->references); - free(dat); -} - -int dat_read(dat_t* dat, const dic_t* dic, FILE *fpi) -{ - uint32_t i = 0, page = 0; - page_header_t ph; - long buffer_size = 0; - uint8_t* buffer = NULL; - - /* Read the whole image. */ - fread_all(fpi, &buffer, &buffer_size); - if (!buffer) { - return 1; - } - - /* Clear Object records. */ - dat_list_finish(&dat->objects); - - /* Read Objects page(s) */ - page = dic->objects.dat_page; - while (page) { - dat_list_read(&dat->objects, &ph, &dic->objects, buffer, PAGESIZE * (page - 1)); - page = ph.next_page; - } - - /* Construct Object UID -> dat->objects[i] mapping table. */ - dat_uidmap_finish(dat->objects_uidmap); - dat->objects_uidmap = dat_uidmap_create(&dat->objects); - - /* Clear Music records. */ - dat_list_finish(&dat->musics); - - /* Read Music page(s) */ - page = dic->music.dat_page; - while (page) { - dat_list_read(&dat->musics, &ph, &dic->music, buffer, PAGESIZE * (page - 1)); - page = ph.next_page; - } - - /* Set filename and pathname fields. */ - for (i = 0;i < dat->musics.num_entries;++i) { - dat_entry_t* entry = &dat->musics.entries[i]; - int index = dat_uidmap_get(dat->objects_uidmap, &dat->objects, entry->fields[IP3DBF_MUSIC_UID].value.dword); - if (0 <= index) { - static const ucs2char_t ucs2cs_root[] = {'/',0}; - ucs2char_t* pathname = NULL; - ucs2char_t* tmp = NULL; - size_t length = 0; - - /* Set the filename. */ - ip3db_variant_set_str( - &entry->fields[IP3DBF_MUSIC_FILENAME], - dat->objects.entries[index].fields[IP3DBF_OBJECTS_OBJECTNAME].value.str - ); - - /* Obtain the pathname by tracing parent UIDs. */ - for (;;) { - uint32_t parent_uid = dat->objects.entries[index].fields[IP3DBF_OBJECTS_PARENTUID].value.dword; - if (parent_uid == 0xFFFFFFFF) { - break; - } - index = dat_uidmap_get(dat->objects_uidmap, &dat->objects, parent_uid); - if (index < 0) { - break; - } - tmp = ucs2append( - dat->objects.entries[index].fields[IP3DBF_OBJECTS_OBJECTNAME].value.str, - pathname - ); - ucs2free(pathname); - pathname = tmp; - } - - tmp = ucs2append(ucs2cs_root, pathname); - ucs2free(pathname); - pathname = tmp; - ip3db_variant_set_str(&entry->fields[IP3DBF_MUSIC_FILEPATH], pathname); - ucs2free(pathname); - } - } - - /* Read References page(s) */ - page = dic->references.dat_page; - while (page) { - dat_list_read(&dat->references, &ph, &dic->references, buffer, PAGESIZE * (page - 1)); - page = ph.next_page; - } - - free(buffer); - return 0; -} - -int dat_write(dat_t* dat, dic_t* dic, FILE *fpo) -{ - uint32_t i = 0, page = 0; - page_header_t ph; - long buffer_size = 0; - uint8_t* buffer = NULL; - - /* Initialize the number of pages as zero. */ - dic->header.num_dat_pages = 0; - dic->objects.dat_page = 0; - dic->music.dat_page = 0; - dic->references.dat_page = 0; - - memset(&ph, 0, sizeof(ph)); - - /* Write Objects page(s) */ - if (0 < dat->objects.num_entries) { - i = 0; - dic->objects.dat_page = page = (dic->header.num_dat_pages + 1); - while (page) { - buffer_size = PAGESIZE * page; - buffer = (uint8_t*)realloc(buffer, sizeof(uint8_t) * buffer_size); - memset(buffer + PAGESIZE * (page - 1), 0, PAGESIZE); - - ph.next_page = page + 1; - dat_list_write(&dat->objects, i, &ph, buffer, PAGESIZE * (page - 1)); - - i += ph.num_entries; - dic->header.num_dat_pages += 1; - page = ph.next_page; - } - } - - /* Clear filepath and filename */ - for (i = 0;i < dat->musics.num_entries;++i) { - static const ucs2char_t empty[] = {0}; - dat_entry_t* entry = &dat->musics.entries[i]; - ip3db_variant_set_str(&entry->fields[IP3DBF_MUSIC_FILEPATH], empty); - ip3db_variant_set_str(&entry->fields[IP3DBF_MUSIC_FILENAME], empty); - } - - /* Write Music page(s) */ - if (0 < dat->musics.num_entries) { - i = 0; - dic->music.dat_page = page = (dic->header.num_dat_pages+1); - while (page) { - buffer_size = PAGESIZE * page; - buffer = (uint8_t*)realloc(buffer, sizeof(uint8_t) * buffer_size); - memset(buffer + PAGESIZE * (page - 1), 0, PAGESIZE); - - ph.next_page = page + 1; - dat_list_write(&dat->musics, i, &ph, buffer, PAGESIZE * (page - 1)); - - i += ph.num_entries; - dic->header.num_dat_pages += 1; - page = ph.next_page; - } - } - - /* Write References page(s) */ - if (0 < dat->references.num_entries) { - i = 0; - dic->references.dat_page = page = (dic->header.num_dat_pages+1); - while (page) { - buffer_size = PAGESIZE * page; - buffer = (uint8_t*)realloc(buffer, sizeof(uint8_t) * buffer_size); - memset(buffer + PAGESIZE * (page - 1), 0, PAGESIZE); - - ph.next_page = page + 1; - dat_list_write(&dat->references, i, &ph, buffer, PAGESIZE * (page - 1)); - - i += ph.num_entries; - dic->header.num_dat_pages += 1; - page = ph.next_page; - } - } - - /* Write out the pages to the file. */ - if (buffer && buffer_size > 0) { - if (fwrite(buffer, 1, buffer_size, fpo) != buffer_size) { - free(buffer); - return 1; - } - } - - free(buffer); - return 0; -} - -void dat_dump(dat_t* dat, const dic_t* dic, FILE *fp) -{ - fprintf(fp, "===== db.dat =====\n"); - fprintf(fp, "OBJECTS = {\n"); - dat_list_dump(&dat->objects, &dic->objects, fp); - fprintf(fp, "}\n"); - fprintf(fp, "MUSIC = {\n"); - dat_list_dump(&dat->musics, &dic->music, fp); - fprintf(fp, "}\n"); - fprintf(fp, "REFERENCES = {\n"); - dat_list_dump(&dat->references, &dic->references, fp); - fprintf(fp, "}\n"); -} - - - -static uint32_t findfile(dat_t* dat, const ucs2char_t *filename) -{ - int i; - const ucs2char_t *filepart = NULL; - ucs2char_t *pathname = alloca(sizeof(ucs2char_t) * (ucs2len(filename) + 1)); - - filepart = ucs2rchr(filename, '/'); - if (!filepart) { - filepart = filename; - } else { - filepart++; - } - - ucs2ncpy(pathname, filename, (filepart-filename)); - pathname[filepart-filename] = 0; - - for (i = 0;i < dat->musics.num_entries;++i) { - dat_entry_t* entry = &dat->musics.entries[i]; - if (ucs2icmp(entry->fields[IP3DBF_MUSIC_FILENAME].value.str, filepart) == 0 && - ucs2icmp(entry->fields[IP3DBF_MUSIC_FILEPATH].value.str, pathname) == 0) { - return entry->fields[IP3DBF_MUSIC_UID].value.dword; - } - } - return 0; -} - -typedef struct { - ucs2char_t* path; - uint32_t uid; -} dircache_element_t; - -typedef struct { - int max_elems; - int num_elems; - dircache_element_t* elems; -} dircache_t; - -static void dircache_init(dircache_t* dc) -{ - memset(dc, 0, sizeof(*dc)); -} - -static void dircache_finish(dircache_t* dc) -{ - int i; - for (i = 0;i < dc->max_elems;++i) { - ucs2free(dc->elems[i].path); - } - free(dc->elems); -} - -static void dircache_push(dircache_t* dc, const ucs2char_t* path, uint32_t uid) -{ - dircache_element_t* elem = NULL; - - if (dc->max_elems < dc->num_elems + 1) { - dc->elems = (dircache_element_t*)realloc(dc->elems, sizeof(dircache_element_t) * (dc->max_elems+1)); - memset(&dc->elems[dc->max_elems], 0, sizeof(dircache_element_t)); - ++dc->max_elems; - } - - elem = &dc->elems[dc->num_elems++]; - ucs2free(elem->path); - elem->path = ucs2dup(path); - elem->uid = uid; -} - -static void dircache_pop(dircache_t* dc, int i) -{ - int n = ++i; - for (;i < dc->num_elems;++i) { - ucs2free(dc->elems[i].path); - memset(&dc->elems[i], 0, sizeof(dc->elems[0])); - } - dc->num_elems = n; -} - -static int dircache_findprefix(dircache_t* dc, const ucs2char_t* path) -{ - int i = dc->num_elems; - while (--i >= 0) { - if (ucs2ncmp(path, dc->elems[i].path, ucs2len(dc->elems[i].path)) == 0) { - break; - } - } - return i; -} - -static dircache_element_t *dircache_get(dircache_t* dc, int i) -{ - return &dc->elems[i]; -} - - -typedef struct { - const ucs2char_t* filepath; - const ucs2char_t* filename; - uint8_t filetype; - int index; -} object_record_t; - - -static int comp_pathname(const void *_x, const void *_y) -{ - const object_record_t* x = (const object_record_t*)_x; - const object_record_t* y = (const object_record_t*)_y; - int ret = ucs2cmp(x->filepath, y->filepath); - if (ret == 0) { - return ucs2cmp(x->filename, y->filename); - } else { - return ret; - } -} - - - -static const ucs2char_t* skip_one_directory(const ucs2char_t* path) -{ - ucs2char_t* p = ucs2chr(path, '/'); - return p ? p+1 : NULL; -} - -void dat_set(dat_t* dat, dic_t* dic, const ip3db_music_record_t* records, int num_records, ip3db_playlist_t* playlists, int num_playlists) -{ - /* Procedure: - * 1) Construct the object chunk and attach music records with Object UIDs. - * Because the object chunk stores a tree structure of path names, we need to - * split a path name to elements and allocate an Object UID to each element. - * For example, the path name "/Music/Beatles/Love/01_love.ogg" will generate - * five objects each of which links to the object of the parent directory: - * - UID=0xFFFFFFFF: "/a/" (root directory; FileType = 0) - * - UID=1 : "Music/" (FileType = 1) - * - UID=2 : "Beatles/" (FileType = 1) - * - UID=3 : "Love/" (FileType = 1) - * - UID=4 : "01_love.ogg" (FileType = 2) - * In order to convert a list of path names to the tree structure, this - * implementation sorts the path names in alphabetical order and finds new - * path elements by using a directory queue (dircache_t). - * - * 3) Attach Object UIDs for file names (FileType = 2) to music records. - * These UIDs are stored in records in the music chunk so that the player - * can refer to the path/file name of a music track quickly. - * - * 4) Construct the music chunk by basically duplicating the records. - * - * Now the content of db.dat is ready. Note that the path character in db.dat - * is not '\\' but '/'. - */ - - int i, j; - dat_entry_t* entry; - uint32_t uid = 0; - static const uint32_t uid_root = 0xFFFFFFFF; - static const ucs2char_t ucs2cs_object_root[] = {'/','a','/',0}; - static const ucs2char_t ucs2cs_root[] = {'/', 0}; - dat_list_t* dato = &dat->objects; - dat_list_t* datm = &dat->musics; - dat_list_t* datr = &dat->references; - uint32_t num_objects = num_records + num_playlists; - object_record_t *objects = (object_record_t*)malloc(sizeof(object_record_t) * num_objects); - dircache_t dc; - - dircache_init(&dc); - - /* Clear all entries. */ - dat_list_finish(dato); - dat_list_finish(datm); - dat_list_finish(datr); - dat_uidmap_finish(dat->objects_uidmap); - - /* Append an entry for the root directory. */ - entry = dat_list_expand(dato); - dat_entry_init(entry, &dic->objects); - ip3db_variant_set_str(&entry->fields[IP3DBF_OBJECTS_OBJECTNAME], ucs2cs_object_root); - ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_UID], uid_root); - - /* Register the root node to the directory cache. */ - dircache_push(&dc, ucs2cs_root, uid_root); - - /* Sort the records in alphabetical order of their path names. */ - for (i = 0;i < num_records;++i) { - objects[i].filepath = records[i][IP3DBF_MUSIC_FILEPATH].value.str; - objects[i].filename = records[i][IP3DBF_MUSIC_FILENAME].value.str; - objects[i].filetype = 2; - objects[i].index = i; - } - for (i = 0;i < num_playlists;++i) { - objects[i+num_records].filepath = playlists[i].filepath; - objects[i+num_records].filename = playlists[i].filename; - objects[i+num_records].filetype = 4; - objects[i+num_records].index = i; - } - qsort(objects, num_objects, sizeof(objects[0]), comp_pathname); - - /* Loop for the records. */ - for (i = 0;i < num_objects;++i) { - /* - * Split a path name into two parts: a prefix that have already been - * registered in the Object table: and a postfix that is being registered - * as Object records. - */ - const ucs2char_t* path = objects[i].filepath; - const ucs2char_t* file = objects[i].filename; - int k = dircache_findprefix(&dc, path); - const dircache_element_t* com = dircache_get(&dc, k); - const ucs2char_t* p = path + ucs2len(com->path); /* the prefix */ - uint32_t puid = com->uid; /* the UID of the parent directory of the postfix */ - - /* Discard directory portions that do not share a prefix with the target. */ - dircache_pop(&dc, k); - - /* Create objects one by one for the directory portions in the postfix. */ - while (p && *p) { - ucs2char_t tmp[MAX_PATH]; - const ucs2char_t* q = skip_one_directory(p); - uid = dato->num_entries; - - /* A directory element (e.g., "Beatles/") */ - ucs2ncpy(tmp, p, q-p); - tmp[q-p] = 0; - - /* Create a new object. */ - entry = dat_list_expand(dato); - dat_entry_init(entry, &dic->objects); - ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_UID], uid); - ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_PARENTUID], puid); - ip3db_variant_set_word(&entry->fields[IP3DBF_OBJECTS_FILETYPE], 1); - ip3db_variant_set_str(&entry->fields[IP3DBF_OBJECTS_OBJECTNAME], tmp); - - /* Register the fullpath (e.g., "/Music/eatles/" in the dircache. */ - ucs2ncpy(tmp, path, q-path); - tmp[q-path] = 0; - dircache_push(&dc, tmp, uid); - - /* Store the current UID for children. */ - puid = uid; - - /* Move to the next portion. */ - p = q; - } - - /* Create a new object for the file name (FileType = 2). */ - if (objects[i].filetype == 2) { - /* Music file. */ - const ip3db_variant_t* record = records[objects[i].index]; - - uid = dato->num_entries; - entry = dat_list_expand(dato); - dat_entry_init(entry, &dic->objects); - ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_UID], uid); - ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_PARENTUID], puid); - ip3db_variant_set_word(&entry->fields[IP3DBF_OBJECTS_FILETYPE], 2); - ip3db_variant_set_str(&entry->fields[IP3DBF_OBJECTS_OBJECTNAME], file); - - /* Create a music record with UID referring to the file name. */ - entry = dat_list_expand(datm); - dat_entry_init(entry, &dic->music); - for (j = 0;j < entry->num_fields;++j) { - ip3db_variant_clone(&entry->fields[j], &record[j]); - } - ip3db_variant_set_dword(&entry->fields[IP3DBF_MUSIC_UID], uid); - } else if (objects[i].filetype == 4) { - /* Playlist file. */ - ip3db_playlist_t* pl = &playlists[objects[i].index]; - - pl->uid = dato->num_entries; - entry = dat_list_expand(dato); - dat_entry_init(entry, &dic->objects); - ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_UID], pl->uid); - ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_PARENTUID], puid); - ip3db_variant_set_word(&entry->fields[IP3DBF_OBJECTS_FILETYPE], 4); - ip3db_variant_set_str(&entry->fields[IP3DBF_OBJECTS_OBJECTNAME], file); - } - } - - /* Loop for playlists. */ - for (i = 0;i < num_playlists;++i) { - const ip3db_playlist_t* pl = &playlists[i]; - for (j = 0;j < pl->num_entries;++j) { - entry = dat_list_expand(datr); - dat_entry_init(entry, &dic->references); - - ip3db_variant_set_dword(&entry->fields[IP3DBF_REFERENCES_PARENTCLUSTER], pl->uid); - ip3db_variant_set_dword(&entry->fields[IP3DBF_REFERENCES_CHILDCLUSTER], findfile(dat, pl->entries[j])); - ip3db_variant_set_word(&entry->fields[IP3DBF_REFERENCES_FILEFORMAT], 0x3009); - } - } - - dircache_finish(&dc); - - dat->objects_uidmap = dat_uidmap_create(&dat->objects); -} +/* + * Low-level library for db.dat. + * + * Copyright (c) 2005-2007 Naoaki Okazaki + * + * This library 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 2.1 of the License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* $Id$ */ + +/* +Brief summary of db.dat structure: +- 0x00000000-0x0001FFFF: object (path name) chunk +- 0x00020000-0x0003FFFF: music (media information) chunk +- Each chunk has a 16-bytes header at the beginning +- Each chunk has an array of offsets to actual entries at the end (backward) +- Field names in db.dat are defined in db.dic (Music and Objects) +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif/*HAVE_CONFIG_H*/ + +#include <os.h> +#include <stdio.h> +#include <stdlib.h> +#include <memory.h> +#include <pmplib/ucs2char.h> + +#include "serialize.h" +#include "util.h" +#include "ip3db.h" +#include "dic.h" +#include "dat.h" + +#define PAGESIZE 0x00020000 +#define COMP(a, b) ((a)>(b))-((a)<(b)) + +typedef struct { + uint32_t size; + uint32_t num_entries; + uint32_t unknown1; + uint32_t next_page; +} page_header_t; + +struct tag_sort_index_t { + const void* base; + int index; +}; + +static void dat_entry_init(dat_entry_t* entry, const dic_table_t* dic_list) +{ + memset(entry, 0, sizeof(*entry)); + entry->fields = (ip3db_variant_t*)malloc(sizeof(ip3db_variant_t) * dic_list->num_fields); + if (entry->fields) { + int i; + entry->num_fields = dic_list->num_fields; + for (i = 0;i < entry->num_fields;++i) { + ip3db_variant_init(&entry->fields[i], dic_list->fields[i].type); + } + } +} + +static void dat_entry_finish(dat_entry_t* entry) +{ + if (entry) { + int i; + for (i = 0;i < entry->num_fields;++i) { + ip3db_variant_finish(&entry->fields[i]); + } + free(entry->fields); + memset(entry, 0, sizeof(*entry)); + } +} + +static size_t dat_entry_size(dat_entry_t* entry) +{ + int i; + size_t size = 0; + for (i = 0;i < entry->num_fields;++i) { + ip3db_variant_t* var = &entry->fields[i]; + switch (var->type) { + case IP3DBVT_STRING: + size += var->value.str ? sizeof(ucs2char_t) * (ucs2len(var->value.str) + 1) : sizeof(ucs2char_t); + break; + case IP3DBVT_BYTE: + size += sizeof(uint8_t); + break; + case IP3DBVT_WORD: + size += sizeof(uint16_t); + break; + case IP3DBVT_DWORD: + size += sizeof(uint32_t); + break; + } + } + return size; +} + +static size_t dat_entry_serialize(dat_entry_t* entry, uint8_t* block, int is_storing) +{ + int i; + uint8_t *p = block; + + /* Serialize all fields in this entry. */ + for (i = 0;i < entry->num_fields;++i) { + ip3db_variant_t* var = &entry->fields[i]; + switch (var->type) { + case IP3DBVT_STRING: + if (is_storing) { + if (var->value.str) { + p += (serialize_ucs2be_string_var(p, var->value.str, is_storing) + 1) * sizeof(ucs2char_t); + } else { + ucs2char_t v = 0; + p += serialize_ucs2be(p, &v, is_storing); + } + } else { + p += (serialize_ucs2be_string_var_alloc(p, &var->value.str) + 1) * sizeof(ucs2char_t); + } + break; + case IP3DBVT_BYTE: + p += serialize_uint8(p, &var->value.byte, is_storing); + break; + case IP3DBVT_WORD: + p += serialize_uint16be(p, &var->value.word, is_storing); + break; + case IP3DBVT_DWORD: + p += serialize_uint32be(p, &var->value.dword, is_storing); + break; + } + } + + return (size_t)(p - block); +} + +static void dat_entry_dump(dat_entry_t* entry, const dic_table_t* dic_list, FILE *fp) +{ + int i; + + /* Loop for all fields in this entry. */ + for (i = 0;i < entry->num_fields;++i) { + ip3db_variant_t* var = &entry->fields[i]; + + /* Output the field name. */ + fprints(fp, " %s: ", dic_list->fields[i].name); + + /* Output its value. */ + switch (var->type) { + case IP3DBVT_STRING: + fprints(fp, "%s\n", var->value.str); + break; + case IP3DBVT_BYTE: + fprintf(fp, "0x%02X\n", var->value.byte); + break; + case IP3DBVT_WORD: + fprintf(fp, "%d\n", var->value.word); + break; + case IP3DBVT_DWORD: + fprintf(fp, "%d\n", var->value.dword); + break; + } + } +} + + + +static void dat_list_init(dat_list_t* list) +{ + memset(list, 0, sizeof(*list)); +} + +static void dat_list_finish(dat_list_t* list) +{ + if (list) { + if (list->entries) { + uint32_t i; + for (i = 0;i < list->num_entries;++i) { + dat_entry_finish(&list->entries[i]); + } + free(list->entries); + } + memset(list, 0, sizeof(*list)); + } +} + +static dat_entry_t *dat_list_expand(dat_list_t* list) +{ + list->entries = (dat_entry_t*)realloc(list->entries, sizeof(dat_entry_t) * (list->num_entries+1)); + return &list->entries[list->num_entries++]; +} + +static size_t dat_list_read(dat_list_t* list, page_header_t* header, const dic_table_t* dic_list, uint8_t* buffer, uint32_t start) +{ + uint32_t i; + uint8_t *p = buffer + start; + uint8_t *q = buffer + start + PAGESIZE - sizeof(uint32_t); + + /* Read the header. */ + p += serialize_uint32be(p, &header->size, 0); + p += serialize_uint32be(p, &header->num_entries, 0); + p += serialize_uint32be(p, &header->unknown1, 0); + p += serialize_uint32be(p, &header->next_page, 0); + + /* Expand the array of records. */ + list->entries = (dat_entry_t*)realloc(list->entries, sizeof(dat_entry_t) * (list->num_entries + header->num_entries)); + for (i = 0;i < header->num_entries;++i) { + dat_entry_init(&list->entries[list->num_entries+i], dic_list); + } + + /* Read the new records. */ + for (i = 0;i < header->num_entries;++i) { + uint32_t offset = 0; + dat_entry_t* entry = &list->entries[list->num_entries+i]; + + /* Read the offset table. */ + q -= serialize_uint32be(q, &offset, 0); + entry->offset = offset + start; + + p += dat_entry_serialize(entry, buffer + entry->offset, 0); + } + + list->num_entries += header->num_entries; + return (size_t)(header->size); +} + +static size_t dat_list_write(dat_list_t* list, uint32_t i, page_header_t* header, uint8_t* buffer, uint32_t start) +{ + uint8_t *p = buffer + start; + uint8_t *q = buffer + start + PAGESIZE - sizeof(uint32_t); + + header->size = 0; + header->num_entries = 0; + + /* Skip the header for now when writing. */ + p += sizeof(uint32_t) * 4; + + /* Write records. */ + while (i < list->num_entries) { + uint32_t offset = 0; + size_t free_space = (size_t)(q-p); + dat_entry_t* entry = &list->entries[i]; + if (free_space < dat_entry_size(entry)) { + break; + } + entry->offset = (uint32_t)(p - buffer); /* compute the current offset address */ + + offset = entry->offset - start; + q -= serialize_uint32be(q, &offset, 1); + + p += dat_entry_serialize(entry, p, 1); + header->num_entries++; + i++; + } + + /* Compute the block size and write the header. */ + header->size = (uint32_t)(p - (buffer + start)); + if (list->num_entries <= i) { + header->next_page = 0; + } + + p = buffer + start; + p += serialize_uint32be(p, &header->size, 1); + p += serialize_uint32be(p, &header->num_entries, 1); + p += serialize_uint32be(p, &header->unknown1, 1); + p += serialize_uint32be(p, &header->next_page, 1); + return header->size; +} + +static void dat_list_dump(dat_list_t* list, const dic_table_t* dic_list, FILE *fp) +{ + uint32_t i; + for (i = 0;i < list->num_entries;++i) { + dat_entry_t* entry = &list->entries[i]; + fprintf(fp, " ENTRY %d (0x%08X) = {\n", i, entry->offset); + dat_entry_dump(entry, dic_list, fp); + fprintf(fp, " }\n"); + } +} + + + +static int comp_object_uid(const void *__x, const void *__y) +{ + const sort_index_t* _x = (const sort_index_t*)__x; + const sort_index_t* _y = (const sort_index_t*)__y; + const dat_list_t* _xb = (const dat_list_t*)_x->base; + const dat_list_t* _yb = (const dat_list_t*)_y->base; + const ip3db_variant_t* x = _xb->entries[_x->index].fields; + const ip3db_variant_t* y = _yb->entries[_y->index].fields; + return COMP(x[IP3DBF_OBJECTS_UID].value.dword, y[IP3DBF_OBJECTS_UID].value.dword); +} + +static sort_index_t* dat_uidmap_create(dat_list_t* list) +{ + int i; + sort_index_t* si = (sort_index_t*)malloc(sizeof(sort_index_t) * list->num_entries); + + if (si) { + /* Sort UIDs. */ + for (i = 0;i < list->num_entries;++i) { + si[i].base = list; + si[i].index = i; + } + qsort(si, list->num_entries, sizeof(si[0]), comp_object_uid); + } + return si; +} + +static void dat_uidmap_finish(sort_index_t* si) +{ + free(si); +} + +static int dat_uidmap_get(sort_index_t* si, dat_list_t* list, uint32_t uid) +{ + int low = 0, high = list->num_entries-1; + + /* Binary search. */ + while (low <= high) { + int middle = (low + high) / 2; + int comp = COMP(uid, list->entries[si[middle].index].fields[IP3DBF_OBJECTS_UID].value.dword); + if (comp == 0) { + /* Found */ + return si[middle].index; + } else if (comp < 0) { + high = middle - 1; + } else { + low = middle + 1; + } + } + return -1; +} + +static ucs2char_t* ucs2append(const ucs2char_t* x, const ucs2char_t* y) +{ + ucs2char_t* ret = NULL; + size_t length = 0; + length += x ? ucs2len(x) : 0; + length += y ? ucs2len(y) : 0; + ret = ucs2calloc(sizeof(ucs2char_t) * (length+1)); + if (x) ucs2cat(ret, x); + if (y) ucs2cat(ret, y); + return ret; +} + + + +dat_t* dat_new() +{ + dat_t* dat = (dat_t*)calloc(1, sizeof(dat_t)); + if (dat) { + dat_list_init(&dat->objects); + dat_list_init(&dat->musics); + dat_list_init(&dat->references); + } + return dat; +} + +void dat_finish(dat_t* dat) +{ + dat_list_finish(&dat->objects); + dat_list_finish(&dat->musics); + dat_list_finish(&dat->references); + free(dat); +} + +int dat_read(dat_t* dat, const dic_t* dic, FILE *fpi) +{ + uint32_t i = 0, page = 0; + page_header_t ph; + long buffer_size = 0; + uint8_t* buffer = NULL; + + /* Read the whole image. */ + fread_all(fpi, &buffer, &buffer_size); + if (!buffer) { + return 1; + } + + /* Clear Object records. */ + dat_list_finish(&dat->objects); + + /* Read Objects page(s) */ + page = dic->objects.dat_page; + while (page) { + dat_list_read(&dat->objects, &ph, &dic->objects, buffer, PAGESIZE * (page - 1)); + page = ph.next_page; + } + + /* Construct Object UID -> dat->objects[i] mapping table. */ + dat_uidmap_finish(dat->objects_uidmap); + dat->objects_uidmap = dat_uidmap_create(&dat->objects); + + /* Clear Music records. */ + dat_list_finish(&dat->musics); + + /* Read Music page(s) */ + page = dic->music.dat_page; + while (page) { + dat_list_read(&dat->musics, &ph, &dic->music, buffer, PAGESIZE * (page - 1)); + page = ph.next_page; + } + + /* Set filename and pathname fields. */ + for (i = 0;i < dat->musics.num_entries;++i) { + dat_entry_t* entry = &dat->musics.entries[i]; + int index = dat_uidmap_get(dat->objects_uidmap, &dat->objects, entry->fields[IP3DBF_MUSIC_UID].value.dword); + if (0 <= index) { + static const ucs2char_t ucs2cs_root[] = {'/',0}; + ucs2char_t* pathname = NULL; + ucs2char_t* tmp = NULL; + size_t length = 0; + + /* Set the filename. */ + ip3db_variant_set_str( + &entry->fields[IP3DBF_MUSIC_FILENAME], + dat->objects.entries[index].fields[IP3DBF_OBJECTS_OBJECTNAME].value.str + ); + + /* Obtain the pathname by tracing parent UIDs. */ + for (;;) { + uint32_t parent_uid = dat->objects.entries[index].fields[IP3DBF_OBJECTS_PARENTUID].value.dword; + if (parent_uid == 0xFFFFFFFF) { + break; + } + index = dat_uidmap_get(dat->objects_uidmap, &dat->objects, parent_uid); + if (index < 0) { + break; + } + tmp = ucs2append( + dat->objects.entries[index].fields[IP3DBF_OBJECTS_OBJECTNAME].value.str, + pathname + ); + ucs2free(pathname); + pathname = tmp; + } + + tmp = ucs2append(ucs2cs_root, pathname); + ucs2free(pathname); + pathname = tmp; + ip3db_variant_set_str(&entry->fields[IP3DBF_MUSIC_FILEPATH], pathname); + ucs2free(pathname); + } + } + + /* Read References page(s) */ + page = dic->references.dat_page; + while (page) { + dat_list_read(&dat->references, &ph, &dic->references, buffer, PAGESIZE * (page - 1)); + page = ph.next_page; + } + + free(buffer); + return 0; +} + +int dat_write(dat_t* dat, dic_t* dic, FILE *fpo) +{ + uint32_t i = 0, page = 0; + page_header_t ph; + long buffer_size = 0; + uint8_t* buffer = NULL; + + /* Initialize the number of pages as zero. */ + dic->header.num_dat_pages = 0; + dic->objects.dat_page = 0; + dic->music.dat_page = 0; + dic->references.dat_page = 0; + + memset(&ph, 0, sizeof(ph)); + + /* Write Objects page(s) */ + if (0 < dat->objects.num_entries) { + i = 0; + dic->objects.dat_page = page = (dic->header.num_dat_pages + 1); + while (page) { + buffer_size = PAGESIZE * page; + buffer = (uint8_t*)realloc(buffer, sizeof(uint8_t) * buffer_size); + memset(buffer + PAGESIZE * (page - 1), 0, PAGESIZE); + + ph.next_page = page + 1; + dat_list_write(&dat->objects, i, &ph, buffer, PAGESIZE * (page - 1)); + + i += ph.num_entries; + dic->header.num_dat_pages += 1; + page = ph.next_page; + } + } + + /* Clear filepath and filename */ + for (i = 0;i < dat->musics.num_entries;++i) { + static const ucs2char_t empty[] = {0}; + dat_entry_t* entry = &dat->musics.entries[i]; + ip3db_variant_set_str(&entry->fields[IP3DBF_MUSIC_FILEPATH], empty); + ip3db_variant_set_str(&entry->fields[IP3DBF_MUSIC_FILENAME], empty); + } + + /* Write Music page(s) */ + if (0 < dat->musics.num_entries) { + i = 0; + dic->music.dat_page = page = (dic->header.num_dat_pages+1); + while (page) { + buffer_size = PAGESIZE * page; + buffer = (uint8_t*)realloc(buffer, sizeof(uint8_t) * buffer_size); + memset(buffer + PAGESIZE * (page - 1), 0, PAGESIZE); + + ph.next_page = page + 1; + dat_list_write(&dat->musics, i, &ph, buffer, PAGESIZE * (page - 1)); + + i += ph.num_entries; + dic->header.num_dat_pages += 1; + page = ph.next_page; + } + } + + /* Write References page(s) */ + if (0 < dat->references.num_entries) { + i = 0; + dic->references.dat_page = page = (dic->header.num_dat_pages+1); + while (page) { + buffer_size = PAGESIZE * page; + buffer = (uint8_t*)realloc(buffer, sizeof(uint8_t) * buffer_size); + memset(buffer + PAGESIZE * (page - 1), 0, PAGESIZE); + + ph.next_page = page + 1; + dat_list_write(&dat->references, i, &ph, buffer, PAGESIZE * (page - 1)); + + i += ph.num_entries; + dic->header.num_dat_pages += 1; + page = ph.next_page; + } + } + + /* Write out the pages to the file. */ + if (buffer && buffer_size > 0) { + if (fwrite(buffer, 1, buffer_size, fpo) != buffer_size) { + free(buffer); + return 1; + } + } + + free(buffer); + return 0; +} + +void dat_dump(dat_t* dat, const dic_t* dic, FILE *fp) +{ + fprintf(fp, "===== db.dat =====\n"); + fprintf(fp, "OBJECTS = {\n"); + dat_list_dump(&dat->objects, &dic->objects, fp); + fprintf(fp, "}\n"); + fprintf(fp, "MUSIC = {\n"); + dat_list_dump(&dat->musics, &dic->music, fp); + fprintf(fp, "}\n"); + fprintf(fp, "REFERENCES = {\n"); + dat_list_dump(&dat->references, &dic->references, fp); + fprintf(fp, "}\n"); +} + + + +static uint32_t findfile(dat_t* dat, const ucs2char_t *filename) +{ + int i; + const ucs2char_t *filepart = NULL; + ucs2char_t *pathname = alloca(sizeof(ucs2char_t) * (ucs2len(filename) + 1)); + + filepart = ucs2rchr(filename, '/'); + if (!filepart) { + filepart = filename; + } else { + filepart++; + } + + ucs2ncpy(pathname, filename, (filepart-filename)); + pathname[filepart-filename] = 0; + + for (i = 0;i < dat->musics.num_entries;++i) { + dat_entry_t* entry = &dat->musics.entries[i]; + if (ucs2icmp(entry->fields[IP3DBF_MUSIC_FILENAME].value.str, filepart) == 0 && + ucs2icmp(entry->fields[IP3DBF_MUSIC_FILEPATH].value.str, pathname) == 0) { + return entry->fields[IP3DBF_MUSIC_UID].value.dword; + } + } + return 0; +} + +typedef struct { + ucs2char_t* path; + uint32_t uid; +} dircache_element_t; + +typedef struct { + int max_elems; + int num_elems; + dircache_element_t* elems; +} dircache_t; + +static void dircache_init(dircache_t* dc) +{ + memset(dc, 0, sizeof(*dc)); +} + +static void dircache_finish(dircache_t* dc) +{ + int i; + for (i = 0;i < dc->max_elems;++i) { + ucs2free(dc->elems[i].path); + } + free(dc->elems); +} + +static void dircache_push(dircache_t* dc, const ucs2char_t* path, uint32_t uid) +{ + dircache_element_t* elem = NULL; + + if (dc->max_elems < dc->num_elems + 1) { + dc->elems = (dircache_element_t*)realloc(dc->elems, sizeof(dircache_element_t) * (dc->max_elems+1)); + memset(&dc->elems[dc->max_elems], 0, sizeof(dircache_element_t)); + ++dc->max_elems; + } + + elem = &dc->elems[dc->num_elems++]; + ucs2free(elem->path); + elem->path = ucs2dup(path); + elem->uid = uid; +} + +static void dircache_pop(dircache_t* dc, int i) +{ + int n = ++i; + for (;i < dc->num_elems;++i) { + ucs2free(dc->elems[i].path); + memset(&dc->elems[i], 0, sizeof(dc->elems[0])); + } + dc->num_elems = n; +} + +static int dircache_findprefix(dircache_t* dc, const ucs2char_t* path) +{ + int i = dc->num_elems; + while (--i >= 0) { + if (ucs2ncmp(path, dc->elems[i].path, ucs2len(dc->elems[i].path)) == 0) { + break; + } + } + return i; +} + +static dircache_element_t *dircache_get(dircache_t* dc, int i) +{ + return &dc->elems[i]; +} + + +typedef struct { + const ucs2char_t* filepath; + const ucs2char_t* filename; + uint8_t filetype; + int index; +} object_record_t; + + +static int comp_pathname(const void *_x, const void *_y) +{ + const object_record_t* x = (const object_record_t*)_x; + const object_record_t* y = (const object_record_t*)_y; + int ret = ucs2cmp(x->filepath, y->filepath); + if (ret == 0) { + return ucs2cmp(x->filename, y->filename); + } else { + return ret; + } +} + + + +static const ucs2char_t* skip_one_directory(const ucs2char_t* path) +{ + ucs2char_t* p = ucs2chr(path, '/'); + return p ? p+1 : NULL; +} + +void dat_set(dat_t* dat, dic_t* dic, const ip3db_music_record_t* records, int num_records, ip3db_playlist_t* playlists, int num_playlists) +{ + /* Procedure: + * 1) Construct the object chunk and attach music records with Object UIDs. + * Because the object chunk stores a tree structure of path names, we need to + * split a path name to elements and allocate an Object UID to each element. + * For example, the path name "/Music/Beatles/Love/01_love.ogg" will generate + * five objects each of which links to the object of the parent directory: + * - UID=0xFFFFFFFF: "/a/" (root directory; FileType = 0) + * - UID=1 : "Music/" (FileType = 1) + * - UID=2 : "Beatles/" (FileType = 1) + * - UID=3 : "Love/" (FileType = 1) + * - UID=4 : "01_love.ogg" (FileType = 2) + * In order to convert a list of path names to the tree structure, this + * implementation sorts the path names in alphabetical order and finds new + * path elements by using a directory queue (dircache_t). + * + * 3) Attach Object UIDs for file names (FileType = 2) to music records. + * These UIDs are stored in records in the music chunk so that the player + * can refer to the path/file name of a music track quickly. + * + * 4) Construct the music chunk by basically duplicating the records. + * + * Now the content of db.dat is ready. Note that the path character in db.dat + * is not '\\' but '/'. + */ + + int i, j; + dat_entry_t* entry; + uint32_t uid = 0; + static const uint32_t uid_root = 0xFFFFFFFF; + static const ucs2char_t ucs2cs_object_root[] = {'/','a','/',0}; + static const ucs2char_t ucs2cs_root[] = {'/', 0}; + dat_list_t* dato = &dat->objects; + dat_list_t* datm = &dat->musics; + dat_list_t* datr = &dat->references; + uint32_t num_objects = num_records + num_playlists; + object_record_t *objects = (object_record_t*)malloc(sizeof(object_record_t) * num_objects); + dircache_t dc; + + dircache_init(&dc); + + /* Clear all entries. */ + dat_list_finish(dato); + dat_list_finish(datm); + dat_list_finish(datr); + dat_uidmap_finish(dat->objects_uidmap); + + /* Append an entry for the root directory. */ + entry = dat_list_expand(dato); + dat_entry_init(entry, &dic->objects); + ip3db_variant_set_str(&entry->fields[IP3DBF_OBJECTS_OBJECTNAME], ucs2cs_object_root); + ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_UID], uid_root); + + /* Register the root node to the directory cache. */ + dircache_push(&dc, ucs2cs_root, uid_root); + + /* Sort the records in alphabetical order of their path names. */ + for (i = 0;i < num_records;++i) { + objects[i].filepath = records[i][IP3DBF_MUSIC_FILEPATH].value.str; + objects[i].filename = records[i][IP3DBF_MUSIC_FILENAME].value.str; + objects[i].filetype = 2; + objects[i].index = i; + } + for (i = 0;i < num_playlists;++i) { + objects[i+num_records].filepath = playlists[i].filepath; + objects[i+num_records].filename = playlists[i].filename; + objects[i+num_records].filetype = 4; + objects[i+num_records].index = i; + } + qsort(objects, num_objects, sizeof(objects[0]), comp_pathname); + + /* Loop for the records. */ + for (i = 0;i < num_objects;++i) { + /* + * Split a path name into two parts: a prefix that have already been + * registered in the Object table: and a postfix that is being registered + * as Object records. + */ + const ucs2char_t* path = objects[i].filepath; + const ucs2char_t* file = objects[i].filename; + int k = dircache_findprefix(&dc, path); + const dircache_element_t* com = dircache_get(&dc, k); + const ucs2char_t* p = path + ucs2len(com->path); /* the prefix */ + uint32_t puid = com->uid; /* the UID of the parent directory of the postfix */ + + /* Discard directory portions that do not share a prefix with the target. */ + dircache_pop(&dc, k); + + /* Create objects one by one for the directory portions in the postfix. */ + while (p && *p) { + ucs2char_t tmp[MAX_PATH]; + const ucs2char_t* q = skip_one_directory(p); + uid = dato->num_entries; + + /* A directory element (e.g., "Beatles/") */ + ucs2ncpy(tmp, p, q-p); + tmp[q-p] = 0; + + /* Create a new object. */ + entry = dat_list_expand(dato); + dat_entry_init(entry, &dic->objects); + ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_UID], uid); + ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_PARENTUID], puid); + ip3db_variant_set_word(&entry->fields[IP3DBF_OBJECTS_FILETYPE], 1); + ip3db_variant_set_str(&entry->fields[IP3DBF_OBJECTS_OBJECTNAME], tmp); + + /* Register the fullpath (e.g., "/Music/eatles/" in the dircache. */ + ucs2ncpy(tmp, path, q-path); + tmp[q-path] = 0; + dircache_push(&dc, tmp, uid); + + /* Store the current UID for children. */ + puid = uid; + + /* Move to the next portion. */ + p = q; + } + + /* Create a new object for the file name (FileType = 2). */ + if (objects[i].filetype == 2) { + /* Music file. */ + const ip3db_variant_t* record = records[objects[i].index]; + + uid = dato->num_entries; + entry = dat_list_expand(dato); + dat_entry_init(entry, &dic->objects); + ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_UID], uid); + ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_PARENTUID], puid); + ip3db_variant_set_word(&entry->fields[IP3DBF_OBJECTS_FILETYPE], 2); + ip3db_variant_set_str(&entry->fields[IP3DBF_OBJECTS_OBJECTNAME], file); + + /* Create a music record with UID referring to the file name. */ + entry = dat_list_expand(datm); + dat_entry_init(entry, &dic->music); + for (j = 0;j < entry->num_fields;++j) { + ip3db_variant_clone(&entry->fields[j], &record[j]); + } + ip3db_variant_set_dword(&entry->fields[IP3DBF_MUSIC_UID], uid); + } else if (objects[i].filetype == 4) { + /* Playlist file. */ + ip3db_playlist_t* pl = &playlists[objects[i].index]; + + pl->uid = dato->num_entries; + entry = dat_list_expand(dato); + dat_entry_init(entry, &dic->objects); + ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_UID], pl->uid); + ip3db_variant_set_dword(&entry->fields[IP3DBF_OBJECTS_PARENTUID], puid); + ip3db_variant_set_word(&entry->fields[IP3DBF_OBJECTS_FILETYPE], 4); + ip3db_variant_set_str(&entry->fields[IP3DBF_OBJECTS_OBJECTNAME], file); + } + } + + /* Loop for playlists. */ + for (i = 0;i < num_playlists;++i) { + const ip3db_playlist_t* pl = &playlists[i]; + for (j = 0;j < pl->num_entries;++j) { + entry = dat_list_expand(datr); + dat_entry_init(entry, &dic->references); + + ip3db_variant_set_dword(&entry->fields[IP3DBF_REFERENCES_PARENTCLUSTER], pl->uid); + ip3db_variant_set_dword(&entry->fields[IP3DBF_REFERENCES_CHILDCLUSTER], findfile(dat, pl->entries[j])); + ip3db_variant_set_word(&entry->fields[IP3DBF_REFERENCES_FILEFORMAT], 0x3009); + } + } + + dircache_finish(&dc); + + dat->objects_uidmap = dat_uidmap_create(&dat->objects); +} Property changes on: trunk/pmplib/lib/pmp_iriverplus3/dat.c ___________________________________________________________________ Name: svn:eol-style + native Modified: trunk/pmplib/lib/pmp_iriverplus3/dat.h =================================================================== --- trunk/pmplib/lib/pmp_iriverplus3/dat.h 2007-02-12 21:14:08 UTC (rev 358) +++ trunk/pmplib/lib/pmp_iriverplus3/dat.h 2007-02-13 21:22:10 UTC (rev 359) @@ -1,54 +1,54 @@ -/* - * Low-level library for db.dat. - * - * Copyright (c) 2005-2007 Naoaki Okazaki - * - * This library 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 2.1 of the License, or (at your option) any later version. - * - * This library 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 this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -/* $Id$ */ - -#ifndef __IP3DB_DAT_H__ -#define __IP3DB_DAT_H__ - -typedef struct { - int num_fields; - ip3db_variant_t *fields; - uint32_t offset; -} dat_entry_t; - -typedef struct { - uint32_t num_entries; - dat_entry_t* entries; -} dat_list_t; - -struct tag_sort_index_t; typedef struct tag_sort_index_t sort_index_t; - -struct tag_dat_t { - dat_list_t objects; - dat_list_t musics; - dat_list_t references; - sort_index_t* objects_uidmap; -}; - -dat_t* dat_new(); -void dat_finish(dat_t* dat); -int dat_read(dat_t* dat, const dic_t* dic, FILE *fpi); -int dat_write(dat_t* dat, dic_t* dic, FILE *fpo); -void dat_dump(dat_t* dat, const dic_t* dic, FILE *fp); -void dat_set(dat_t* dat, dic_t* dic, const ip3db_music_record_t* records, int num_records, ip3db_playlist_t* playlists, int num_playlists); - -#endif/*__IP3DB_DAT_H__*/ +/* + * Low-level library for db.dat. + * + * Copyright (c) 2005-2007 Naoaki Okazaki + * + * This library 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 2.1 of the License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* $Id$ */ + +#ifndef __IP3DB_DAT_H__ +#define __IP3DB_DAT_H__ + +typedef struct { + int num_fields; + ip3db_variant_t *fields; + uint32_t offset; +} dat_entry_t; + +typedef struct { + uint32_t num_entries; + dat_entry_t* entries; +} dat_list_t; + +struct tag_sort_index_t; typedef struct tag_sort_index_t sort_index_t; + +struct tag_dat_t { + dat_list_t objects; + dat_list_t musics; + dat_list_t references; + sort_index_t* objects_uidmap; +}; + +dat_t* dat_new(); +void dat_finish(dat_t* dat); +int dat_read(dat_t* dat, const dic_t* dic, FILE *fpi); +int dat_write(dat_t* dat, dic_t* dic, FILE *fpo); +void dat_dump(dat_t* dat, const dic_t* dic, FILE *fp); +void dat_set(dat_t* dat, dic_t* dic, const ip3db_music_record_t* records, int num_records, ip3db_playlist_t* playlists, int num_playlists); + +#endif/*__IP3DB_DAT_H__*/ Property changes on: trunk/pmplib/lib/pmp_iriverplus3/dat.h ___________________________________________________________________ Name: svn:eol-style + native Modified: trunk/pmplib/lib/pmp_iriverplus3/dic.c =================================================================== --- trunk/pmplib/lib/pmp_iriverplus3/dic.c 2007-02-12 21:14:08 UTC (rev 358) +++ trunk/pmplib/lib/pmp_iriverplus3/dic.c 2007-02-13 21:22:10 UTC (rev 359) @@ -1,530 +1,530 @@ -/* - * Low-level library for db.dic. - * - * Copyright (c) 2005-2007 Naoaki Okazaki - * - * This library 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 2.1 of the License, or (at your option) any later version. - * - * This library 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 this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -/* $Id$ */ - -/* -Some important findings from db.dic: -- This file defines field names/types in a database. -- This file stores offset addresses of root nodes in db.idx. -*/ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif/*HAVE_CONFIG_H*/ - -#include <os.h> -#include <stdio.h> -#include <stdlib.h> -#include <memory.h> -#include <pmplib/ucs2char.h> - -#include "serialize.h" -#include "util.h" -#include "ip3db.h" -#include "dic.h" - -static dic_index_t music_indices[] = { - {0, 0x0086, {IP3DBF_MUSIC_TITLE, IP3DBF_MUSIC_NONE, IP3DBF_MUSIC_NONE}}, - {0, 0x00F8, {IP3DBF_MUSIC_RATING, IP3DBF_MUSIC_NONE, IP3DBF_MUSIC_NONE}}, - {0, 0x0152, {IP3DBF_MUSIC_TRACKNUMBER, IP3DBF_MUSIC_NONE, IP3DBF_MUSIC_NONE}}, - {0, 0x022C, {IP3DBF_MUSIC_CHANGEDFLAG, IP3DBF_MUSIC_NONE, IP3DBF_MUSIC_NONE}}, - {0, 0x027A, {IP3DBF_MUSIC_CLUSM, IP3DBF_MUSIC_NONE, IP3DBF_MUSIC_NONE}}, - {0, 0x031C, {IP3DBF_MUSIC_UID, IP3DBF_MUSIC_NONE, IP3DBF_MUSIC_NONE}}, - {0, 0x0352, {IP3DBF_MUSIC_GENRE, IP3DBF_MUSIC_ARTIST, IP3DBF_MUSIC_TITLE}}, - {0, 0x0384, {IP3DBF_MUSIC_ARTIST, IP3DBF_MUSIC_ALBUM, IP3DBF_MUSIC_TRACKNUMBER}}, - {0, 0x03C2, {IP3DBF_MUSIC_ARTIST, IP3DBF_MUSIC_TRACKNUMBER, IP3DBF_MUSIC_NONE}}, - {0, 0x03F4, {IP3DBF_MUSIC_ARTIST, IP3DBF_MUSIC_TITLE, IP3DBF_MUSIC_NONE}}, - {0, 0x041A, {IP3DBF_MUSIC_GENRE, IP3DBF_MUSIC_TITLE, IP3DBF_MUSIC_NONE}}, - {0, 0x043E, {IP3DBF_MUSIC_ALBUM, IP3DBF_MUSIC_TRACKNUMBER, IP3DBF_MUSIC_NONE}}, -}; - -static dic_index_t references_indices[] = { - {0, 0x0764, {IP3DBF_REFERENCES_PARENTCLUSTER, IP3DBF_REFERENCES_NONE, IP3DBF_REFERENCES_NONE}}, - {0, 0x078C, {IP3DBF_REFERENCES_CHILDCLUSTER, IP3DBF_REFERENCES_NONE, IP3DBF_REFERENCES_NONE}}, -}; - -static dic_index_t objects_indices[] = { - {0, 0x099A, {IP3DBF_OBJECTS_UID, IP3DBF_MUSIC_NONE, IP3DBF_MUSIC_NONE}}, - {0, 0x09F0, {IP3DBF_OBJECTS_FILETYPE, IP3DBF_MUSIC_NONE, IP3DBF_MUSIC_NONE}}, - {0, 0x0A0E, {IP3DBF_OBJECTS_OBJECTNAME, IP3DBF_MUSIC_NONE, IP3DBF_MUSIC_NONE}}, - {0, 0x0ACE, {IP3DBF_OBJECTS_FILETYPE, IP3DBF_OBJECTS_PARENTUID, IP3DBF_OBJECTS_PROPERTIES}}, -}; - -static int dic_serialize(dic_t* dic, uint8_t* buffer, int is_storing); - -static void dic_field_init(dic_field_t* entry) -{ - memset(entry, 0, sizeof(*entry)); -} - -static void dic_field_finish(dic_field_t* entry) -{ - ucs2free(entry->name); - dic_field_init(entry); -} - -static size_t dic_field_serialize(uint8_t* block, dic_field_t* entry, int is_storing) -{ - uint8_t *p = block; - p += s... [truncated message content] |