From cb8c79541504ce8a4e1d4243ccec826707e30544 Mon Sep 17 00:00:00 2001 From: Miika Pekkarinen Date: Thu, 20 Jul 2006 12:19:31 +0000 Subject: [PATCH] Support importing runtimedb data from ascii files. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10260 a1c6a512-1295-4272-9138-f99709370657 --- apps/debug_menu.c | 4 +- apps/lang/english.lang | 14 + apps/settings_menu.c | 1 + apps/tagcache.c | 645 ++++++++++++++++++++++++++++------------- apps/tagcache.h | 9 +- apps/tagtree.c | 15 + apps/tagtree.h | 1 + 7 files changed, 491 insertions(+), 198 deletions(-) diff --git a/apps/debug_menu.c b/apps/debug_menu.c index d426356d11..724bab90ab 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -1877,7 +1877,9 @@ static bool dbg_tagcache_info(void) lcd_clear_display(); stat = tagcache_get_stat(); - snprintf(buf, sizeof(buf), "Busy: %s", stat->initialized ? "No" : "Yes"); + snprintf(buf, sizeof(buf), "Initialized: %s", stat->initialized ? "Yes" : "No"); + lcd_puts(0, line++, buf); + snprintf(buf, sizeof(buf), "DB Ready: %s", stat->ready ? "Yes" : "No"); lcd_puts(0, line++, buf); snprintf(buf, sizeof(buf), "RAM Cache: %s", stat->ramcache ? "Yes" : "No"); lcd_puts(0, line++, buf); diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 78e4ab6147..d05549a423 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -8655,4 +8655,18 @@ *: "" + + id: LANG_TAGCACHE_IMPORT + desc: in tag cache settings + user: + + *: "Import modifications" + + + *: "Import modifications" + + + *: "Import modifications" + + diff --git a/apps/settings_menu.c b/apps/settings_menu.c index c85075d0b2..8417e77341 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c @@ -1570,6 +1570,7 @@ static bool tagcache_settings_menu(void) { ID2P(LANG_TAGCACHE_UPDATE), tagcache_update }, { ID2P(LANG_RUNTIMEDB_ACTIVE), tagcache_runtimedb }, { ID2P(LANG_TAGCACHE_EXPORT), tagtree_export }, + { ID2P(LANG_TAGCACHE_IMPORT), tagtree_import }, }; m=menu_init( items, sizeof(items) / sizeof(*items), NULL, diff --git a/apps/tagcache.c b/apps/tagcache.c index dd52a3d716..8df7aef8de 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c @@ -61,6 +61,10 @@ static const int unique_tags[] = { tag_artist, tag_album, tag_genre, tag_compose static const int numeric_tags[] = { tag_year, tag_tracknumber, tag_length, tag_bitrate, tag_playcount, tag_playtime, tag_lastplayed, tag_virt_autoscore }; +static const char *tags_str[] = { "artist", "album", "genre", "title", + "filename", "composer", "year", "tracknumber", "bitrate", "length", + "playcount", "playtime", "lastplayed" }; + /* Status information of the tagcache. */ static struct tagcache_stat stat; @@ -77,33 +81,41 @@ enum tagcache_queue { /* Variable-length tag entry in tag files. */ struct tagfile_entry { - short tag_length; - short idx_id; - char tag_data[0]; + short tag_length; /* Length of the data in bytes including '\0' */ + short idx_id; /* Corresponding entry location in index file of not unique tags */ + char tag_data[0]; /* Begin of the tag data */ }; /* Fixed-size tag entry in master db index. */ struct index_entry { - long tag_seek[TAG_COUNT]; - long flag; + long tag_seek[TAG_COUNT]; /* Location of tag data or numeric tag data */ + long flag; /* Status flags */ }; /* Header is the same in every file. */ struct tagcache_header { - long magic; - long datasize; - long entry_count; + long magic; /* Header version number */ + long datasize; /* Data size in bytes */ + long entry_count; /* Number of entries in this file */ }; +struct master_header { + struct tagcache_header tch; + long serial; /* Increasing counting number */ +}; + +static long current_serial; + #ifdef HAVE_TC_RAMCACHE /* Header is created when loading database to ram. */ struct ramcache_header { - struct tagcache_header h; - struct index_entry *indices; - char *tags[TAG_COUNT]; - int entry_count[TAG_COUNT]; + struct master_header h; /* Header from the master index */ + struct index_entry *indices; /* Master index file content */ + char *tags[TAG_COUNT]; /* Tag file content (not including filename tag) */ + int entry_count[TAG_COUNT]; /* Number of entries in the indices. */ }; +/* Pointer to allocated ramcache_header */ static struct ramcache_header *hdr; #endif @@ -140,6 +152,24 @@ static int total_entry_count = 0; static int data_size = 0; static int processed_dir_count; +int tagcache_str_to_tag(const char *str) +{ + int i; + + for (i = 0; i < (long)(sizeof(tags_str)/sizeof(tags_str[0])); i++) + { + if (!strcasecmp(tags_str[i], str)) + return i; + } + + return -1; +} + +const char* tagcache_tag_to_str(int tag) +{ + return tags_str[tag]; +} + bool tagcache_is_numeric_tag(int type) { int i; @@ -207,7 +237,7 @@ static long find_entry_ram(const char *filename, else i = 0; - for (; i < hdr->h.entry_count; i++) + for (; i < hdr->h.tch.entry_count; i++) { if (hdr->indices[i].tag_seek[tag_filename] == (long)dc) { @@ -257,7 +287,7 @@ static long find_entry_disk(const char *filename) if (last_pos > 0) lseek(fd, last_pos, SEEK_SET); else - lseek(fd, sizeof(struct tagcache_header), SEEK_SET); + lseek(fd, sizeof(struct master_header), SEEK_SET); while (true) { @@ -320,7 +350,7 @@ static long find_entry_disk(const char *filename) return tfe.idx_id; } -bool tagcache_find_index(struct tagcache_search *tcs, const char *filename) +static int find_index(const char *filename) { long idx_id = -1; @@ -335,6 +365,17 @@ bool tagcache_find_index(struct tagcache_search *tcs, const char *filename) if (idx_id < 0) return false; + return idx_id; +} + +bool tagcache_find_index(struct tagcache_search *tcs, const char *filename) +{ + int idx_id; + + idx_id = find_index(filename); + if (idx_id < 0) + return false; + if (!tagcache_search(tcs, tag_filename)) return false; @@ -359,7 +400,7 @@ static bool tagcache_get_index(const struct tagcache_search *tcs, #endif lseek(tcs->masterfd, idxid * sizeof(struct index_entry) - + sizeof(struct tagcache_header), SEEK_SET); + + sizeof(struct master_header), SEEK_SET); if (read(tcs->masterfd, idx, sizeof(struct index_entry)) != sizeof(struct index_entry)) { @@ -453,7 +494,7 @@ static bool build_lookup_list(struct tagcache_search *tcs) { int j; - for (i = tcs->seek_pos; i < hdr->h.entry_count; i++) + for (i = tcs->seek_pos; i < hdr->h.tch.entry_count; i++) { if (tcs->seek_list_count == SEEK_LIST_SIZE) break ; @@ -531,7 +572,7 @@ static bool build_lookup_list(struct tagcache_search *tcs) #endif lseek(tcs->masterfd, tcs->seek_pos * sizeof(struct index_entry) + - sizeof(struct tagcache_header), SEEK_SET); + sizeof(struct master_header), SEEK_SET); while (read(tcs->masterfd, &entry, sizeof(struct index_entry)) == sizeof(struct index_entry)) @@ -612,9 +653,53 @@ static bool build_lookup_list(struct tagcache_search *tcs) } +static void remove_files(void) +{ + int i; + char buf[MAX_PATH]; + + stat.ready = false; + remove(TAGCACHE_FILE_MASTER); + for (i = 0; i < TAG_COUNT; i++) + { + if (tagcache_is_numeric_tag(i)) + continue; + + snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, i); + remove(buf); + } +} + +static int open_master_fd(struct master_header *hdr, bool write) +{ + int fd; + + fd = open(TAGCACHE_FILE_MASTER, write ? O_RDWR : O_RDONLY); + if (fd < 0) + { + logf("master file open failed for R/W"); + stat.ready = false; + return fd; + } + + /* Check the header. */ + read(fd, hdr, sizeof(struct master_header)); + if (hdr->tch.magic != TAGCACHE_MAGIC) + { + logf("header error"); + stat.ready = false; + close(fd); + remove_files(); + return -2; + } + + return fd; +} + bool tagcache_search(struct tagcache_search *tcs, int tag) { - struct tagcache_header h; + struct tagcache_header tag_hdr; + struct master_header master_hdr; char buf[MAX_PATH]; int i; @@ -622,7 +707,7 @@ bool tagcache_search(struct tagcache_search *tcs, int tag) tagcache_search_finish(tcs); memset(tcs, 0, sizeof(struct tagcache_search)); - if (stat.commit_step > 0) + if (stat.commit_step > 0 || !stat.ready) return false; tcs->position = sizeof(struct tagcache_header); @@ -659,29 +744,17 @@ bool tagcache_search(struct tagcache_search *tcs, int tag) } /* Check the header. */ - if (read(tcs->idxfd[tcs->type], &h, sizeof(struct tagcache_header)) != - sizeof(struct tagcache_header) || h.magic != TAGCACHE_MAGIC) + if (read(tcs->idxfd[tcs->type], &tag_hdr, sizeof(struct tagcache_header)) != + sizeof(struct tagcache_header) || tag_hdr.magic != TAGCACHE_MAGIC) { logf("incorrect header"); return false; } - tcs->masterfd = open(TAGCACHE_FILE_MASTER, O_RDONLY); + tcs->masterfd = open_master_fd(&master_hdr, false); if (tcs->masterfd < 0) - { - logf("open fail"); return false; - } - - if (read(tcs->masterfd, &h, sizeof(struct tagcache_header)) != - sizeof(struct tagcache_header) || h.magic != TAGCACHE_MAGIC) - { - logf("header error"); - close(tcs->masterfd); - tcs->masterfd = -1; - return false; - } } return true; @@ -1164,22 +1237,6 @@ static void add_tagcache(const char *path) total_entry_count++; } -static void remove_files(void) -{ - int i; - char buf[MAX_PATH]; - - remove(TAGCACHE_FILE_MASTER); - for (i = 0; i < TAG_COUNT; i++) - { - if (tagcache_is_numeric_tag(i)) - continue; - - snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, i); - remove(buf); - } -} - static bool tempbuf_insert(char *str, int id, int idx_id, bool unique) { struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf; @@ -1377,7 +1434,7 @@ inline static int tempbuf_find_location(int id) static bool build_numeric_index(int index_type, struct tagcache_header *h, int tmpfd) { - struct tagcache_header tch; + struct master_header tcmh; struct index_entry idx; int masterfd; int masterfd_pos; @@ -1416,23 +1473,10 @@ static bool build_numeric_index(int index_type, struct tagcache_header *h, int t } /* Update the entries in index. */ - masterfd = open(TAGCACHE_FILE_MASTER, O_RDWR); - - if (masterfd < 0) - { - logf("No master file found!"); + if ( (masterfd = open_master_fd(&tcmh, true)) < 0) return false; - } - if (read(masterfd, &tch, sizeof(struct tagcache_header)) != - sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC) - { - logf("header error"); - close(masterfd); - return false; - } - - masterfd_pos = lseek(masterfd, tch.entry_count * sizeof(struct index_entry), + masterfd_pos = lseek(masterfd, tcmh.tch.entry_count * sizeof(struct index_entry), SEEK_CUR); if (masterfd_pos == filesize(masterfd)) { @@ -1481,6 +1525,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd) { int i; struct tagcache_header tch; + struct master_header tcmh; struct index_entry idxbuf[IDX_BUF_DEPTH]; int idxbuf_pos; char buf[MAX_PATH]; @@ -1617,12 +1662,14 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd) } /* Write the header (write real values later). */ - tch = *h; - tch.entry_count = 0; - tch.datasize = 0; - write(masterfd, &tch, sizeof(struct tagcache_header)); + memset(&tcmh, 0, sizeof(struct master_header)); + tcmh.tch = *h; + tcmh.tch.entry_count = 0; + tcmh.tch.datasize = 0; + write(masterfd, &tcmh, sizeof(struct master_header)); init = true; masterfd_pos = lseek(masterfd, 0, SEEK_CUR); + current_serial = 0; } else { @@ -1632,8 +1679,8 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd) */ init = false; - if (read(masterfd, &tch, sizeof(struct tagcache_header)) != - sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC) + if (read(masterfd, &tcmh, sizeof(struct master_header)) != + sizeof(struct master_header) || tcmh.tch.magic != TAGCACHE_MAGIC) { logf("header error"); close(fd); @@ -1648,7 +1695,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd) * However, if the index is sorted, we need to update all tag * pointers in the master file for the current index. */ - masterfd_pos = lseek(masterfd, tch.entry_count * sizeof(struct index_entry), + masterfd_pos = lseek(masterfd, tcmh.tch.entry_count * sizeof(struct index_entry), SEEK_CUR); if (masterfd_pos == filesize(masterfd)) { @@ -1698,7 +1745,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd) if (tagcache_is_unique_tag(index_type)) error = !tempbuf_insert(buf, i, -1, true); else - error = !tempbuf_insert(buf, i, tch.entry_count + i, false); + error = !tempbuf_insert(buf, i, tcmh.tch.entry_count + i, false); if (error) { @@ -1724,13 +1771,13 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd) * Now update all indexes in the master lookup file. */ logf("updating indices..."); - lseek(masterfd, sizeof(struct tagcache_header), SEEK_SET); - for (i = 0; i < tch.entry_count; i += idxbuf_pos) + lseek(masterfd, sizeof(struct master_header), SEEK_SET); + for (i = 0; i < tcmh.tch.entry_count; i += idxbuf_pos) { int j; int loc = lseek(masterfd, 0, SEEK_CUR); - idxbuf_pos = MIN(tch.entry_count - i, IDX_BUF_DEPTH); + idxbuf_pos = MIN(tcmh.tch.entry_count - i, IDX_BUF_DEPTH); if (read(masterfd, idxbuf, sizeof(struct index_entry)*idxbuf_pos) != (int)sizeof(struct index_entry)*idxbuf_pos) @@ -1761,7 +1808,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd) if (idxbuf[j].tag_seek[index_type] < 0) { - logf("update error: %d/%d", i+j, tch.entry_count); + logf("update error: %d/%d", i+j, tcmh.tch.entry_count); error = true; goto error_exit; } @@ -1852,7 +1899,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd) /* Write to index file. */ idxbuf[j].tag_seek[index_type] = lseek(fd, 0, SEEK_CUR); fe.tag_length = entry.tag_length[index_type]; - fe.idx_id = tch.entry_count + i + j; + fe.idx_id = tcmh.tch.entry_count + i + j; write(fd, &fe, sizeof(struct tagfile_entry)); write(fd, buf, fe.tag_length); tempbufidx++; @@ -1910,7 +1957,8 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd) static bool commit(void) { - struct tagcache_header header, header_old; + struct tagcache_header tch; + struct master_header tcmh; int i, len, rc; int tmpfd; int masterfd; @@ -1930,9 +1978,9 @@ static bool commit(void) /* Load the header. */ len = sizeof(struct tagcache_header); - rc = read(tmpfd, &header, len); + rc = read(tmpfd, &tch, len); - if (header.magic != TAGCACHE_MAGIC || rc != len) + if (tch.magic != TAGCACHE_MAGIC || rc != len) { logf("incorrect header"); close(tmpfd); @@ -1941,7 +1989,7 @@ static bool commit(void) return false; } - if (header.entry_count == 0) + if (tch.entry_count == 0) { logf("nothing to commit"); close(tmpfd); @@ -1987,11 +2035,11 @@ static bool commit(void) return false; } - logf("commit %d entries...", header.entry_count); + logf("commit %d entries...", tch.entry_count); /* Now create the index files. */ stat.commit_step = 0; - header.datasize = 0; + tch.datasize = 0; stat.commit_delayed = false; for (i = 0; i < TAG_COUNT; i++) @@ -2001,11 +2049,11 @@ static bool commit(void) stat.commit_step++; if (tagcache_is_numeric_tag(i)) { - build_numeric_index(i, &header, tmpfd); + build_numeric_index(i, &tch, tmpfd); continue; } - ret = build_index(i, &header, tmpfd); + ret = build_index(i, &tch, tmpfd); if (ret <= 0) { close(tmpfd); @@ -2023,33 +2071,21 @@ static bool commit(void) stat.commit_step = 0; /* Update the master index headers. */ - masterfd = open(TAGCACHE_FILE_MASTER, O_RDWR); - if (masterfd < 0) - { - logf("failed to open master index"); + if ( (masterfd = open_master_fd(&tcmh, true)) < 0) return false; - } - if (read(masterfd, &header_old, sizeof(struct tagcache_header)) - != sizeof(struct tagcache_header) || - header_old.magic != TAGCACHE_MAGIC) - { - logf("incorrect header"); - close(masterfd); - remove_files(); - return false; - } - - header.entry_count += header_old.entry_count; - /* Datasize has been recalculated. */ - // header.datasize += header_old.datasize; + tcmh.tch.entry_count += tch.entry_count; + tcmh.tch.datasize = sizeof(struct master_header) + + sizeof(struct index_entry) * tcmh.tch.entry_count + + tch.datasize; lseek(masterfd, 0, SEEK_SET); - write(masterfd, &header, sizeof(struct tagcache_header)); + write(masterfd, &tcmh, sizeof(struct master_header)); close(masterfd); logf("tagcache committed"); remove(TAGCACHE_FILE_TEMP); + stat.ready = true; if (local_allocation) { @@ -2090,27 +2126,95 @@ static void free_tempbuf(void) tempbuf_size = 0; } -static int open_master_fd(struct tagcache_header *hdr) +static bool update_current_serial(long serial) { + struct master_header myhdr; int fd; - fd = open(TAGCACHE_FILE_MASTER, O_RDWR); - if (fd < 0) + if ( (fd = open_master_fd(&myhdr, true)) < 0) + return false; + + myhdr.serial = serial; + current_serial = serial; + +#ifdef HAVE_TC_RAMCACHE + if (hdr) + hdr->h.serial = serial; +#endif + + /* Write it back */ + lseek(fd, 0, SEEK_SET); + write(fd, &myhdr, sizeof(struct master_header)); + close(fd); + + return true; +} + +long tagcache_increase_serial(void) +{ + if (!update_current_serial(current_serial + 1)) + return -1; + + return current_serial; +} + +long tagcache_get_serial(void) +{ + return current_serial; +} + +static bool modify_numeric_entry(int masterfd, int idx_id, int tag, long data) +{ + struct index_entry idx; + + if (!tagcache_is_numeric_tag(tag)) + return false; + +#ifdef HAVE_TC_RAMCACHE + /* Update ram entries first. */ + if (hdr) { - logf("master file open failed for R/W"); - return fd; + hdr->indices[idx_id].tag_seek[tag] = data; + hdr->indices[idx_id].flag |= FLAG_DIRTYNUM; + } +#endif + + /* And now update the db on disk also. */ + lseek(masterfd, idx_id * sizeof(struct index_entry) + + sizeof(struct master_header), SEEK_SET); + if (read(masterfd, &idx, sizeof(struct index_entry)) + != sizeof(struct index_entry)) + { + logf("read error"); + return false; } - /* Check the header. */ - read(fd, hdr, sizeof(struct tagcache_header)); - if (hdr->magic != TAGCACHE_MAGIC) + idx.flag |= FLAG_DIRTYNUM; + idx.tag_seek[tag] = data; + + lseek(masterfd, -sizeof(struct index_entry), SEEK_CUR); + if (write(masterfd, &idx, sizeof(struct index_entry)) + != sizeof(struct index_entry)) { - logf("header error"); - close(fd); - return -2; + logf("write error"); + return false; } - return fd; + return true; +} + +bool tagcache_modify_numeric_entry(struct tagcache_search *tcs, + int tag, long data) +{ + struct master_header myhdr; + + if (tcs->masterfd < 0) + { + if ( (tcs->masterfd = open_master_fd(&myhdr, true)) < 0) + return false; + } + + return modify_numeric_entry(tcs->masterfd, tcs->idx_id, tag, data); } static bool write_tag(int fd, const char *tagstr, const char *datastr) @@ -2141,13 +2245,192 @@ static bool write_tag(int fd, const char *tagstr, const char *datastr) return true; } +static bool read_tag(char *dest, long size, + const char *src, const char *tagstr) +{ + int pos; + char current_tag[32]; + + while (*src != '\0') + { + /* Skip all whitespace */ + while (*src == ' ') + src++; + + if (*src == '\0') + break; + + pos = 0; + /* Read in tag name */ + while (*src != '=' && *src != ' ') + { + current_tag[pos] = *src; + src++; + pos++; + + if (*src == '\0' || pos >= (long)sizeof(current_tag)) + return false; + } + current_tag[pos] = '\0'; + + /* Read in tag data */ + + /* Find the start. */ + while (*src != '"' && *src != '\0') + src++; + + if (*src == '\0' || *(++src) == '\0') + return false; + + /* Read the data. */ + for (pos = 0; pos < size; pos++) + { + if (*src == '\0') + break; + + if (*src == '\\' && *(src+1) == '"') + { + dest[pos] = '"'; + src += 2; + continue; + } + + dest[pos] = *src; + + if (*src == '"') + { + src++; + break; + } + + if (*src == '\0') + break; + + src++; + } + dest[pos] = '\0'; + + if (!strcasecmp(tagstr, current_tag)) + return true; + } + + return false; +} + +static bool parse_changelog_line(int masterfd, const char *buf) +{ + char tag_data[MAX_PATH]; + int idx_id; + const int import_tags[] = { tag_playcount, tag_playtime, tag_lastplayed }; + int i; + + if (*buf == '#') + return true; + + if (!read_tag(tag_data, sizeof tag_data, buf, "filename")) + { + logf("filename missing"); + logf("-> %s", buf); + return false; + } + + idx_id = find_index(tag_data); + if (idx_id < 0) + { + logf("entry not found"); + return false; + } + + for (i = 0; i < (long)(sizeof(import_tags)/sizeof(import_tags[0])); i++) + { + int data; + + if (!read_tag(tag_data, sizeof tag_data, buf, + tagcache_tag_to_str(import_tags[i]))) + { + continue; + } + + data = atoi(tag_data); + if (data < 0) + continue; + + modify_numeric_entry(masterfd, idx_id, import_tags[i], data); + + if (import_tags[i] == tag_lastplayed && data > current_serial) + current_serial = data; + } + + return true; +} + +bool tagcache_import_changelog(void) +{ + struct master_header myhdr; + int clfd, masterfd; + char buf[512]; + int pos = 0; + + clfd = open(TAGCACHE_FILE_CHANGELOG, O_RDONLY); + if (clfd < 0) + { + logf("failure to open changelog"); + return false; + } + + if ( (masterfd = open_master_fd(&myhdr, true)) < 0) + { + close(clfd); + return false; + } + + /* Fast readline */ + while ( 1 ) + { + char *p; + char *next = NULL; + int rc; + + rc = read(clfd, &buf[pos], sizeof(buf)-pos-1); + if (rc >= 0) + buf[pos+rc] = '\0'; + + if ( (p = strchr(buf, '\r')) != NULL) + { + *p = '\0'; + next = ++p; + } + else + p = buf; + + if ( (p = strchr(p, '\n')) != NULL) + { + *p = '\0'; + next = ++p; + } + + parse_changelog_line(masterfd, buf); + + if (next) + { + pos = sizeof(buf) - ((long)next - (long)buf) - 1; + memmove(buf, next, pos); + } + else + break ; + } + + close(clfd); + close(masterfd); + + update_current_serial(current_serial); + + return true; +} + bool tagcache_create_changelog(struct tagcache_search *tcs) { - static const char *tags_str[] = { "artist", "album", "genre", "title", - "filename", "playcount", "playtime", "lastplayed" }; - static const int tags[] = { tag_artist, tag_album, tag_genre, tag_title, - tag_filename, tag_playcount, tag_playtime, tag_lastplayed }; - struct tagcache_header myhdr; + struct master_header myhdr; struct index_entry idx; char buf[256]; char temp[32]; @@ -2167,18 +2450,18 @@ bool tagcache_create_changelog(struct tagcache_search *tcs) if (tcs->masterfd < 0) { - if ( (tcs->masterfd = open_master_fd(&myhdr)) < 0) + if ( (tcs->masterfd = open_master_fd(&myhdr, false)) < 0) return false; } else { lseek(tcs->masterfd, 0, SEEK_SET); - read(tcs->masterfd, &myhdr, sizeof(struct tagcache_header)); + read(tcs->masterfd, &myhdr, sizeof(struct master_header)); } write(clfd, "## Changelog version 1\n", 23); - for (i = 0; i < myhdr.entry_count; i++) + for (i = 0; i < myhdr.tch.entry_count; i++) { if (read(tcs->masterfd, &idx, sizeof(struct index_entry)) != sizeof(struct index_entry)) @@ -2196,19 +2479,19 @@ bool tagcache_create_changelog(struct tagcache_search *tcs) logf("Found!"); /* Now retrieve all tags. */ - for (j = 0; j < (long)(sizeof(tags) / sizeof(tags[0])); j++) + for (j = 0; j < TAG_COUNT; j++) { - if (tagcache_is_numeric_tag(tags[j])) + if (tagcache_is_numeric_tag(j)) { - snprintf(temp, sizeof temp, "%d", idx.tag_seek[tags[j]]); - write_tag(clfd, tags_str[j], temp); + snprintf(temp, sizeof temp, "%d", idx.tag_seek[j]); + write_tag(clfd, tagcache_tag_to_str(j), temp); continue; } - tcs->type = tags[j]; + tcs->type = j; tagcache_retrieve(tcs, i, buf, sizeof buf); logf("tag: %s", buf); - write_tag(clfd, tags_str[j], buf); + write_tag(clfd, tagcache_tag_to_str(j), buf); } write(clfd, "\n", 1); @@ -2221,60 +2504,12 @@ bool tagcache_create_changelog(struct tagcache_search *tcs) return true; } -bool tagcache_modify_numeric_entry(struct tagcache_search *tcs, - int tag, long data) -{ - struct index_entry idx; - struct tagcache_header myhdr; - - if (!tagcache_is_numeric_tag(tag)) - return false; - -#ifdef HAVE_TC_RAMCACHE - /* Update ram entries first. */ - if (tcs->ramsearch) - { - hdr->indices[tcs->idx_id].tag_seek[tag] = data; - hdr->indices[tcs->idx_id].flag |= FLAG_DIRTYNUM; - } -#endif - - /* And now update the db on disk also. */ - if (tcs->masterfd < 0) - { - if ( (tcs->masterfd = open_master_fd(&myhdr)) < 0) - return false; - } - - lseek(tcs->masterfd, tcs->idx_id * sizeof(struct index_entry) - + sizeof(struct tagcache_header), SEEK_SET); - if (read(tcs->masterfd, &idx, sizeof(struct index_entry)) - != sizeof(struct index_entry)) - { - logf("read error"); - return false; - } - - idx.flag |= FLAG_DIRTYNUM; - idx.tag_seek[tag] = data; - - lseek(tcs->masterfd, -sizeof(struct index_entry), SEEK_CUR); - if (write(tcs->masterfd, &idx, sizeof(struct index_entry)) - != sizeof(struct index_entry)) - { - logf("write error"); - return false; - } - - return true; -} - static bool delete_entry(long idx_id) { int fd; int tag, i; struct index_entry idx, myidx; - struct tagcache_header myhdr; + struct master_header myhdr; char buf[MAX_PATH]; int in_use[TAG_COUNT]; @@ -2284,7 +2519,7 @@ static bool delete_entry(long idx_id) hdr->indices[idx_id].flag |= FLAG_DELETED; #endif - if ( (fd = open_master_fd(&myhdr) < 0) ) + if ( (fd = open_master_fd(&myhdr, true) < 0) ) return false; lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR); @@ -2310,8 +2545,8 @@ static bool delete_entry(long idx_id) for (tag = 0; tag < TAG_COUNT; tag++) in_use[tag] = 0; - lseek(fd, sizeof(struct tagcache_header), SEEK_SET); - for (i = 0; i < myhdr.entry_count; i++) + lseek(fd, sizeof(struct master_header), SEEK_SET); + for (i = 0; i < myhdr.tch.entry_count; i++) { if (read(fd, &idx, sizeof(struct index_entry)) != sizeof(struct index_entry)) @@ -2394,11 +2629,11 @@ static bool allocate_tagcache(void) /* Load the header. */ hdr = (struct ramcache_header *)(((long)audiobuf & ~0x03) + 0x04); memset(hdr, 0, sizeof(struct ramcache_header)); - len = sizeof(struct tagcache_header); + len = sizeof(struct master_header); rc = read(fd, &hdr->h, len); close(fd); - if (hdr->h.magic != TAGCACHE_MAGIC || rc != len) + if (hdr->h.tch.magic != TAGCACHE_MAGIC || rc != len) { logf("incorrect header"); remove_files(); @@ -2412,8 +2647,7 @@ static bool allocate_tagcache(void) * Now calculate the required cache size plus * some extra space for alignment fixes. */ - stat.ramcache_allocated = hdr->h.datasize + 128 + TAGCACHE_RESERVE + - sizeof(struct index_entry) * hdr->h.entry_count + + stat.ramcache_allocated = hdr->h.tch.datasize + 128 + TAGCACHE_RESERVE + sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *); logf("tagcache: %d bytes allocated.", stat.ramcache_allocated); logf("at: 0x%04x", audiobuf); @@ -2445,20 +2679,18 @@ static bool load_tagcache(void) return false; } - if (read(fd, &hdr->h, sizeof(struct tagcache_header)) - != sizeof(struct tagcache_header) - || hdr->h.magic != TAGCACHE_MAGIC) + if (read(fd, &hdr->h, sizeof(struct master_header)) + != sizeof(struct master_header) + || hdr->h.tch.magic != TAGCACHE_MAGIC) { logf("incorrect header"); return false; } - lseek(fd, sizeof(struct tagcache_header), SEEK_SET); - idx = hdr->indices; /* Load the master index table. */ - for (i = 0; i < hdr->h.entry_count; i++) + for (i = 0; i < hdr->h.tch.entry_count; i++) { rc = read(fd, idx, sizeof(struct index_entry)); if (rc != sizeof(struct index_entry)) @@ -2865,6 +3097,20 @@ static void load_ramcache(void) } #endif +static bool check_master_fd(void) +{ + struct master_header myhdr; + int fd; + + if ( (fd = open_master_fd(&myhdr, false)) < 0) + return false; + + close(fd); + current_serial = myhdr.serial; + + return true; +} + static void tagcache_thread(void) { struct event ev; @@ -2885,6 +3131,8 @@ static void tagcache_thread(void) cpu_boost(false); + stat.ready = check_master_fd(); + stat.initialized = true; while (1) @@ -2964,7 +3212,7 @@ static int get_progress(void) #ifdef HAVE_TC_RAMCACHE { if (hdr && stat.ramcache) - total_count = hdr->h.entry_count; + total_count = hdr->h.tch.entry_count; } #endif @@ -2989,6 +3237,9 @@ void tagcache_start_scan(void) bool tagcache_update(void) { + if (!stat.ready) + return false; + queue_post(&tagcache_queue, Q_UPDATE, 0); gui_syncsplash(HZ*2, true, str(LANG_TAGCACHE_FORCE_UPDATE_SPLASH)); @@ -3019,8 +3270,10 @@ bool tagcache_is_ramcache(void) void tagcache_init(void) { stat.initialized = false; + stat.ready = false; stat.commit_step = 0; filenametag_fd = -1; + current_serial = 0; queue_init(&tagcache_queue); create_thread(tagcache_thread, tagcache_stack, diff --git a/apps/tagcache.h b/apps/tagcache.h index e655088cc2..387f8ab74c 100644 --- a/apps/tagcache.h +++ b/apps/tagcache.h @@ -36,7 +36,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, #define IDX_BUF_DEPTH 64 /* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ -#define TAGCACHE_MAGIC 0x54434805 +#define TAGCACHE_MAGIC 0x54434806 /* How much to allocate extra space for ramcache. */ #define TAGCACHE_RESERVE 32768 @@ -79,6 +79,7 @@ enum modifiers { clause_mod_none, clause_mod_not }; struct tagcache_stat { bool initialized; /* Is tagcache currently busy? */ + bool ready; /* Is tagcache ready to be used? */ bool ramcache; /* Is tagcache loaded in ram? */ bool commit_delayed; /* Has commit been delayed until next reboot? */ int commit_step; /* Commit progress */ @@ -124,6 +125,9 @@ struct tagcache_search { long result_seek; }; +int tagcache_str_to_tag(const char *str); +const char* tagcache_tag_to_str(int tag); + bool tagcache_is_numeric_tag(int type); bool tagcache_is_unique_tag(int type); bool tagcache_is_sorted_tag(int type); @@ -138,6 +142,9 @@ bool tagcache_retrieve(struct tagcache_search *tcs, int idxid, char *buf, long size); void tagcache_search_finish(struct tagcache_search *tcs); long tagcache_get_numeric(const struct tagcache_search *tcs, int tag); +long tagcache_increase_serial(void); +long tagcache_get_serial(void); +bool tagcache_import_changelog(void); bool tagcache_create_changelog(struct tagcache_search *tcs); bool tagcache_modify_numeric_entry(struct tagcache_search *tcs, int tag, long data); diff --git a/apps/tagtree.c b/apps/tagtree.c index f5c100f6ea..7321e9df49 100644 --- a/apps/tagtree.c +++ b/apps/tagtree.c @@ -395,6 +395,10 @@ static void tagtree_unbuffer_event(struct mp3entry *id3, bool last_track) playcount++; + lastplayed = tagcache_increase_serial(); + if (lastplayed < 0) + return; + /* Ignore the last 15s (crossfade etc.) */ playtime += MIN(id3->length, id3->elapsed + 15 * 1000); @@ -427,6 +431,17 @@ bool tagtree_export(void) return false; } +bool tagtree_import(void) +{ + gui_syncsplash(0, true, str(LANG_WAIT)); + if (!tagcache_import_changelog()) + { + gui_syncsplash(HZ*2, true, str(LANG_FAILED)); + } + + return false; +} + void tagtree_init(void) { int fd; diff --git a/apps/tagtree.h b/apps/tagtree.h index 2129176683..e40dd1b45b 100644 --- a/apps/tagtree.h +++ b/apps/tagtree.h @@ -31,6 +31,7 @@ struct tagentry { }; bool tagtree_export(void); +bool tagtree_import(void); void tagtree_init(void); int tagtree_enter(struct tree_context* c); void tagtree_exit(struct tree_context* c);