summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/debug_menu.c4
-rw-r--r--apps/lang/english.lang14
-rw-r--r--apps/settings_menu.c1
-rw-r--r--apps/tagcache.c645
-rw-r--r--apps/tagcache.h9
-rw-r--r--apps/tagtree.c15
-rw-r--r--apps/tagtree.h1
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)
1877 1877
1878 lcd_clear_display(); 1878 lcd_clear_display();
1879 stat = tagcache_get_stat(); 1879 stat = tagcache_get_stat();
1880 snprintf(buf, sizeof(buf), "Busy: %s", stat->initialized ? "No" : "Yes"); 1880 snprintf(buf, sizeof(buf), "Initialized: %s", stat->initialized ? "Yes" : "No");
1881 lcd_puts(0, line++, buf);
1882 snprintf(buf, sizeof(buf), "DB Ready: %s", stat->ready ? "Yes" : "No");
1881 lcd_puts(0, line++, buf); 1883 lcd_puts(0, line++, buf);
1882 snprintf(buf, sizeof(buf), "RAM Cache: %s", stat->ramcache ? "Yes" : "No"); 1884 snprintf(buf, sizeof(buf), "RAM Cache: %s", stat->ramcache ? "Yes" : "No");
1883 lcd_puts(0, line++, buf); 1885 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 @@
8655 *: "" 8655 *: ""
8656 </voice> 8656 </voice>
8657</phrase> 8657</phrase>
8658<phrase>
8659 id: LANG_TAGCACHE_IMPORT
8660 desc: in tag cache settings
8661 user:
8662 <source>
8663 *: "Import modifications"
8664 </source>
8665 <dest>
8666 *: "Import modifications"
8667 </dest>
8668 <voice>
8669 *: "Import modifications"
8670 </voice>
8671</phrase>
8658 8672
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)
1570 { ID2P(LANG_TAGCACHE_UPDATE), tagcache_update }, 1570 { ID2P(LANG_TAGCACHE_UPDATE), tagcache_update },
1571 { ID2P(LANG_RUNTIMEDB_ACTIVE), tagcache_runtimedb }, 1571 { ID2P(LANG_RUNTIMEDB_ACTIVE), tagcache_runtimedb },
1572 { ID2P(LANG_TAGCACHE_EXPORT), tagtree_export }, 1572 { ID2P(LANG_TAGCACHE_EXPORT), tagtree_export },
1573 { ID2P(LANG_TAGCACHE_IMPORT), tagtree_import },
1573 }; 1574 };
1574 1575
1575 m=menu_init( items, sizeof(items) / sizeof(*items), NULL, 1576 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
61static const int numeric_tags[] = { tag_year, tag_tracknumber, tag_length, tag_bitrate, 61static const int numeric_tags[] = { tag_year, tag_tracknumber, tag_length, tag_bitrate,
62 tag_playcount, tag_playtime, tag_lastplayed, tag_virt_autoscore }; 62 tag_playcount, tag_playtime, tag_lastplayed, tag_virt_autoscore };
63 63
64static const char *tags_str[] = { "artist", "album", "genre", "title",
65 "filename", "composer", "year", "tracknumber", "bitrate", "length",
66 "playcount", "playtime", "lastplayed" };
67
64/* Status information of the tagcache. */ 68/* Status information of the tagcache. */
65static struct tagcache_stat stat; 69static struct tagcache_stat stat;
66 70
@@ -77,33 +81,41 @@ enum tagcache_queue {
77 81
78/* Variable-length tag entry in tag files. */ 82/* Variable-length tag entry in tag files. */
79struct tagfile_entry { 83struct tagfile_entry {
80 short tag_length; 84 short tag_length; /* Length of the data in bytes including '\0' */
81 short idx_id; 85 short idx_id; /* Corresponding entry location in index file of not unique tags */
82 char tag_data[0]; 86 char tag_data[0]; /* Begin of the tag data */
83}; 87};
84 88
85/* Fixed-size tag entry in master db index. */ 89/* Fixed-size tag entry in master db index. */
86struct index_entry { 90struct index_entry {
87 long tag_seek[TAG_COUNT]; 91 long tag_seek[TAG_COUNT]; /* Location of tag data or numeric tag data */
88 long flag; 92 long flag; /* Status flags */
89}; 93};
90 94
91/* Header is the same in every file. */ 95/* Header is the same in every file. */
92struct tagcache_header { 96struct tagcache_header {
93 long magic; 97 long magic; /* Header version number */
94 long datasize; 98 long datasize; /* Data size in bytes */
95 long entry_count; 99 long entry_count; /* Number of entries in this file */
100};
101
102struct master_header {
103 struct tagcache_header tch;
104 long serial; /* Increasing counting number */
96}; 105};
97 106
107static long current_serial;
108
98#ifdef HAVE_TC_RAMCACHE 109#ifdef HAVE_TC_RAMCACHE
99/* Header is created when loading database to ram. */ 110/* Header is created when loading database to ram. */
100struct ramcache_header { 111struct ramcache_header {
101 struct tagcache_header h; 112 struct master_header h; /* Header from the master index */
102 struct index_entry *indices; 113 struct index_entry *indices; /* Master index file content */
103 char *tags[TAG_COUNT]; 114 char *tags[TAG_COUNT]; /* Tag file content (not including filename tag) */
104 int entry_count[TAG_COUNT]; 115 int entry_count[TAG_COUNT]; /* Number of entries in the indices. */
105}; 116};
106 117
118/* Pointer to allocated ramcache_header */
107static struct ramcache_header *hdr; 119static struct ramcache_header *hdr;
108#endif 120#endif
109 121
@@ -140,6 +152,24 @@ static int total_entry_count = 0;
140static int data_size = 0; 152static int data_size = 0;
141static int processed_dir_count; 153static int processed_dir_count;
142 154
155int tagcache_str_to_tag(const char *str)
156{
157 int i;
158
159 for (i = 0; i < (long)(sizeof(tags_str)/sizeof(tags_str[0])); i++)
160 {
161 if (!strcasecmp(tags_str[i], str))
162 return i;
163 }
164
165 return -1;
166}
167
168const char* tagcache_tag_to_str(int tag)
169{
170 return tags_str[tag];
171}
172
143bool tagcache_is_numeric_tag(int type) 173bool tagcache_is_numeric_tag(int type)
144{ 174{
145 int i; 175 int i;
@@ -207,7 +237,7 @@ static long find_entry_ram(const char *filename,
207 else 237 else
208 i = 0; 238 i = 0;
209 239
210 for (; i < hdr->h.entry_count; i++) 240 for (; i < hdr->h.tch.entry_count; i++)
211 { 241 {
212 if (hdr->indices[i].tag_seek[tag_filename] == (long)dc) 242 if (hdr->indices[i].tag_seek[tag_filename] == (long)dc)
213 { 243 {
@@ -257,7 +287,7 @@ static long find_entry_disk(const char *filename)
257 if (last_pos > 0) 287 if (last_pos > 0)
258 lseek(fd, last_pos, SEEK_SET); 288 lseek(fd, last_pos, SEEK_SET);
259 else 289 else
260 lseek(fd, sizeof(struct tagcache_header), SEEK_SET); 290 lseek(fd, sizeof(struct master_header), SEEK_SET);
261 291
262 while (true) 292 while (true)
263 { 293 {
@@ -320,7 +350,7 @@ static long find_entry_disk(const char *filename)
320 return tfe.idx_id; 350 return tfe.idx_id;
321} 351}
322 352
323bool tagcache_find_index(struct tagcache_search *tcs, const char *filename) 353static int find_index(const char *filename)
324{ 354{
325 long idx_id = -1; 355 long idx_id = -1;
326 356
@@ -335,6 +365,17 @@ bool tagcache_find_index(struct tagcache_search *tcs, const char *filename)
335 if (idx_id < 0) 365 if (idx_id < 0)
336 return false; 366 return false;
337 367
368 return idx_id;
369}
370
371bool tagcache_find_index(struct tagcache_search *tcs, const char *filename)
372{
373 int idx_id;
374
375 idx_id = find_index(filename);
376 if (idx_id < 0)
377 return false;
378
338 if (!tagcache_search(tcs, tag_filename)) 379 if (!tagcache_search(tcs, tag_filename))
339 return false; 380 return false;
340 381
@@ -359,7 +400,7 @@ static bool tagcache_get_index(const struct tagcache_search *tcs,
359#endif 400#endif
360 401
361 lseek(tcs->masterfd, idxid * sizeof(struct index_entry) 402 lseek(tcs->masterfd, idxid * sizeof(struct index_entry)
362 + sizeof(struct tagcache_header), SEEK_SET); 403 + sizeof(struct master_header), SEEK_SET);
363 if (read(tcs->masterfd, idx, sizeof(struct index_entry)) != 404 if (read(tcs->masterfd, idx, sizeof(struct index_entry)) !=
364 sizeof(struct index_entry)) 405 sizeof(struct index_entry))
365 { 406 {
@@ -453,7 +494,7 @@ static bool build_lookup_list(struct tagcache_search *tcs)
453 { 494 {
454 int j; 495 int j;
455 496
456 for (i = tcs->seek_pos; i < hdr->h.entry_count; i++) 497 for (i = tcs->seek_pos; i < hdr->h.tch.entry_count; i++)
457 { 498 {
458 if (tcs->seek_list_count == SEEK_LIST_SIZE) 499 if (tcs->seek_list_count == SEEK_LIST_SIZE)
459 break ; 500 break ;
@@ -531,7 +572,7 @@ static bool build_lookup_list(struct tagcache_search *tcs)
531#endif 572#endif
532 573
533 lseek(tcs->masterfd, tcs->seek_pos * sizeof(struct index_entry) + 574 lseek(tcs->masterfd, tcs->seek_pos * sizeof(struct index_entry) +
534 sizeof(struct tagcache_header), SEEK_SET); 575 sizeof(struct master_header), SEEK_SET);
535 576
536 while (read(tcs->masterfd, &entry, sizeof(struct index_entry)) == 577 while (read(tcs->masterfd, &entry, sizeof(struct index_entry)) ==
537 sizeof(struct index_entry)) 578 sizeof(struct index_entry))
@@ -612,9 +653,53 @@ static bool build_lookup_list(struct tagcache_search *tcs)
612} 653}
613 654
614 655
656static void remove_files(void)
657{
658 int i;
659 char buf[MAX_PATH];
660
661 stat.ready = false;
662 remove(TAGCACHE_FILE_MASTER);
663 for (i = 0; i < TAG_COUNT; i++)
664 {
665 if (tagcache_is_numeric_tag(i))
666 continue;
667
668 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, i);
669 remove(buf);
670 }
671}
672
673static int open_master_fd(struct master_header *hdr, bool write)
674{
675 int fd;
676
677 fd = open(TAGCACHE_FILE_MASTER, write ? O_RDWR : O_RDONLY);
678 if (fd < 0)
679 {
680 logf("master file open failed for R/W");
681 stat.ready = false;
682 return fd;
683 }
684
685 /* Check the header. */
686 read(fd, hdr, sizeof(struct master_header));
687 if (hdr->tch.magic != TAGCACHE_MAGIC)
688 {
689 logf("header error");
690 stat.ready = false;
691 close(fd);
692 remove_files();
693 return -2;
694 }
695
696 return fd;
697}
698
615bool tagcache_search(struct tagcache_search *tcs, int tag) 699bool tagcache_search(struct tagcache_search *tcs, int tag)
616{ 700{
617 struct tagcache_header h; 701 struct tagcache_header tag_hdr;
702 struct master_header master_hdr;
618 char buf[MAX_PATH]; 703 char buf[MAX_PATH];
619 int i; 704 int i;
620 705
@@ -622,7 +707,7 @@ bool tagcache_search(struct tagcache_search *tcs, int tag)
622 tagcache_search_finish(tcs); 707 tagcache_search_finish(tcs);
623 708
624 memset(tcs, 0, sizeof(struct tagcache_search)); 709 memset(tcs, 0, sizeof(struct tagcache_search));
625 if (stat.commit_step > 0) 710 if (stat.commit_step > 0 || !stat.ready)
626 return false; 711 return false;
627 712
628 tcs->position = sizeof(struct tagcache_header); 713 tcs->position = sizeof(struct tagcache_header);
@@ -659,29 +744,17 @@ bool tagcache_search(struct tagcache_search *tcs, int tag)
659 } 744 }
660 745
661 /* Check the header. */ 746 /* Check the header. */
662 if (read(tcs->idxfd[tcs->type], &h, sizeof(struct tagcache_header)) != 747 if (read(tcs->idxfd[tcs->type], &tag_hdr, sizeof(struct tagcache_header)) !=
663 sizeof(struct tagcache_header) || h.magic != TAGCACHE_MAGIC) 748 sizeof(struct tagcache_header) || tag_hdr.magic != TAGCACHE_MAGIC)
664 { 749 {
665 logf("incorrect header"); 750 logf("incorrect header");
666 return false; 751 return false;
667 } 752 }
668 753
669 tcs->masterfd = open(TAGCACHE_FILE_MASTER, O_RDONLY); 754 tcs->masterfd = open_master_fd(&master_hdr, false);
670 755
671 if (tcs->masterfd < 0) 756 if (tcs->masterfd < 0)
672 {
673 logf("open fail");
674 return false; 757 return false;
675 }
676
677 if (read(tcs->masterfd, &h, sizeof(struct tagcache_header)) !=
678 sizeof(struct tagcache_header) || h.magic != TAGCACHE_MAGIC)
679 {
680 logf("header error");
681 close(tcs->masterfd);
682 tcs->masterfd = -1;
683 return false;
684 }
685 } 758 }
686 759
687 return true; 760 return true;
@@ -1164,22 +1237,6 @@ static void add_tagcache(const char *path)
1164 total_entry_count++; 1237 total_entry_count++;
1165} 1238}
1166 1239
1167static void remove_files(void)
1168{
1169 int i;
1170 char buf[MAX_PATH];
1171
1172 remove(TAGCACHE_FILE_MASTER);
1173 for (i = 0; i < TAG_COUNT; i++)
1174 {
1175 if (tagcache_is_numeric_tag(i))
1176 continue;
1177
1178 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, i);
1179 remove(buf);
1180 }
1181}
1182
1183static bool tempbuf_insert(char *str, int id, int idx_id, bool unique) 1240static bool tempbuf_insert(char *str, int id, int idx_id, bool unique)
1184{ 1241{
1185 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf; 1242 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
@@ -1377,7 +1434,7 @@ inline static int tempbuf_find_location(int id)
1377 1434
1378static bool build_numeric_index(int index_type, struct tagcache_header *h, int tmpfd) 1435static bool build_numeric_index(int index_type, struct tagcache_header *h, int tmpfd)
1379{ 1436{
1380 struct tagcache_header tch; 1437 struct master_header tcmh;
1381 struct index_entry idx; 1438 struct index_entry idx;
1382 int masterfd; 1439 int masterfd;
1383 int masterfd_pos; 1440 int masterfd_pos;
@@ -1416,23 +1473,10 @@ static bool build_numeric_index(int index_type, struct tagcache_header *h, int t
1416 } 1473 }
1417 1474
1418 /* Update the entries in index. */ 1475 /* Update the entries in index. */
1419 masterfd = open(TAGCACHE_FILE_MASTER, O_RDWR); 1476 if ( (masterfd = open_master_fd(&tcmh, true)) < 0)
1420
1421 if (masterfd < 0)
1422 {
1423 logf("No master file found!");
1424 return false; 1477 return false;
1425 }
1426 1478
1427 if (read(masterfd, &tch, sizeof(struct tagcache_header)) != 1479 masterfd_pos = lseek(masterfd, tcmh.tch.entry_count * sizeof(struct index_entry),
1428 sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC)
1429 {
1430 logf("header error");
1431 close(masterfd);
1432 return false;
1433 }
1434
1435 masterfd_pos = lseek(masterfd, tch.entry_count * sizeof(struct index_entry),
1436 SEEK_CUR); 1480 SEEK_CUR);
1437 if (masterfd_pos == filesize(masterfd)) 1481 if (masterfd_pos == filesize(masterfd))
1438 { 1482 {
@@ -1481,6 +1525,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
1481{ 1525{
1482 int i; 1526 int i;
1483 struct tagcache_header tch; 1527 struct tagcache_header tch;
1528 struct master_header tcmh;
1484 struct index_entry idxbuf[IDX_BUF_DEPTH]; 1529 struct index_entry idxbuf[IDX_BUF_DEPTH];
1485 int idxbuf_pos; 1530 int idxbuf_pos;
1486 char buf[MAX_PATH]; 1531 char buf[MAX_PATH];
@@ -1617,12 +1662,14 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
1617 } 1662 }
1618 1663
1619 /* Write the header (write real values later). */ 1664 /* Write the header (write real values later). */
1620 tch = *h; 1665 memset(&tcmh, 0, sizeof(struct master_header));
1621 tch.entry_count = 0; 1666 tcmh.tch = *h;
1622 tch.datasize = 0; 1667 tcmh.tch.entry_count = 0;
1623 write(masterfd, &tch, sizeof(struct tagcache_header)); 1668 tcmh.tch.datasize = 0;
1669 write(masterfd, &tcmh, sizeof(struct master_header));
1624 init = true; 1670 init = true;
1625 masterfd_pos = lseek(masterfd, 0, SEEK_CUR); 1671 masterfd_pos = lseek(masterfd, 0, SEEK_CUR);
1672 current_serial = 0;
1626 } 1673 }
1627 else 1674 else
1628 { 1675 {
@@ -1632,8 +1679,8 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
1632 */ 1679 */
1633 init = false; 1680 init = false;
1634 1681
1635 if (read(masterfd, &tch, sizeof(struct tagcache_header)) != 1682 if (read(masterfd, &tcmh, sizeof(struct master_header)) !=
1636 sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC) 1683 sizeof(struct master_header) || tcmh.tch.magic != TAGCACHE_MAGIC)
1637 { 1684 {
1638 logf("header error"); 1685 logf("header error");
1639 close(fd); 1686 close(fd);
@@ -1648,7 +1695,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
1648 * However, if the index is sorted, we need to update all tag 1695 * However, if the index is sorted, we need to update all tag
1649 * pointers in the master file for the current index. 1696 * pointers in the master file for the current index.
1650 */ 1697 */
1651 masterfd_pos = lseek(masterfd, tch.entry_count * sizeof(struct index_entry), 1698 masterfd_pos = lseek(masterfd, tcmh.tch.entry_count * sizeof(struct index_entry),
1652 SEEK_CUR); 1699 SEEK_CUR);
1653 if (masterfd_pos == filesize(masterfd)) 1700 if (masterfd_pos == filesize(masterfd))
1654 { 1701 {
@@ -1698,7 +1745,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
1698 if (tagcache_is_unique_tag(index_type)) 1745 if (tagcache_is_unique_tag(index_type))
1699 error = !tempbuf_insert(buf, i, -1, true); 1746 error = !tempbuf_insert(buf, i, -1, true);
1700 else 1747 else
1701 error = !tempbuf_insert(buf, i, tch.entry_count + i, false); 1748 error = !tempbuf_insert(buf, i, tcmh.tch.entry_count + i, false);
1702 1749
1703 if (error) 1750 if (error)
1704 { 1751 {
@@ -1724,13 +1771,13 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
1724 * Now update all indexes in the master lookup file. 1771 * Now update all indexes in the master lookup file.
1725 */ 1772 */
1726 logf("updating indices..."); 1773 logf("updating indices...");
1727 lseek(masterfd, sizeof(struct tagcache_header), SEEK_SET); 1774 lseek(masterfd, sizeof(struct master_header), SEEK_SET);
1728 for (i = 0; i < tch.entry_count; i += idxbuf_pos) 1775 for (i = 0; i < tcmh.tch.entry_count; i += idxbuf_pos)
1729 { 1776 {
1730 int j; 1777 int j;
1731 int loc = lseek(masterfd, 0, SEEK_CUR); 1778 int loc = lseek(masterfd, 0, SEEK_CUR);
1732 1779
1733 idxbuf_pos = MIN(tch.entry_count - i, IDX_BUF_DEPTH); 1780 idxbuf_pos = MIN(tcmh.tch.entry_count - i, IDX_BUF_DEPTH);
1734 1781
1735 if (read(masterfd, idxbuf, sizeof(struct index_entry)*idxbuf_pos) != 1782 if (read(masterfd, idxbuf, sizeof(struct index_entry)*idxbuf_pos) !=
1736 (int)sizeof(struct index_entry)*idxbuf_pos) 1783 (int)sizeof(struct index_entry)*idxbuf_pos)
@@ -1761,7 +1808,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
1761 1808
1762 if (idxbuf[j].tag_seek[index_type] < 0) 1809 if (idxbuf[j].tag_seek[index_type] < 0)
1763 { 1810 {
1764 logf("update error: %d/%d", i+j, tch.entry_count); 1811 logf("update error: %d/%d", i+j, tcmh.tch.entry_count);
1765 error = true; 1812 error = true;
1766 goto error_exit; 1813 goto error_exit;
1767 } 1814 }
@@ -1852,7 +1899,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
1852 /* Write to index file. */ 1899 /* Write to index file. */
1853 idxbuf[j].tag_seek[index_type] = lseek(fd, 0, SEEK_CUR); 1900 idxbuf[j].tag_seek[index_type] = lseek(fd, 0, SEEK_CUR);
1854 fe.tag_length = entry.tag_length[index_type]; 1901 fe.tag_length = entry.tag_length[index_type];
1855 fe.idx_id = tch.entry_count + i + j; 1902 fe.idx_id = tcmh.tch.entry_count + i + j;
1856 write(fd, &fe, sizeof(struct tagfile_entry)); 1903 write(fd, &fe, sizeof(struct tagfile_entry));
1857 write(fd, buf, fe.tag_length); 1904 write(fd, buf, fe.tag_length);
1858 tempbufidx++; 1905 tempbufidx++;
@@ -1910,7 +1957,8 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
1910 1957
1911static bool commit(void) 1958static bool commit(void)
1912{ 1959{
1913 struct tagcache_header header, header_old; 1960 struct tagcache_header tch;
1961 struct master_header tcmh;
1914 int i, len, rc; 1962 int i, len, rc;
1915 int tmpfd; 1963 int tmpfd;
1916 int masterfd; 1964 int masterfd;
@@ -1930,9 +1978,9 @@ static bool commit(void)
1930 1978
1931 /* Load the header. */ 1979 /* Load the header. */
1932 len = sizeof(struct tagcache_header); 1980 len = sizeof(struct tagcache_header);
1933 rc = read(tmpfd, &header, len); 1981 rc = read(tmpfd, &tch, len);
1934 1982
1935 if (header.magic != TAGCACHE_MAGIC || rc != len) 1983 if (tch.magic != TAGCACHE_MAGIC || rc != len)
1936 { 1984 {
1937 logf("incorrect header"); 1985 logf("incorrect header");
1938 close(tmpfd); 1986 close(tmpfd);
@@ -1941,7 +1989,7 @@ static bool commit(void)
1941 return false; 1989 return false;
1942 } 1990 }
1943 1991
1944 if (header.entry_count == 0) 1992 if (tch.entry_count == 0)
1945 { 1993 {
1946 logf("nothing to commit"); 1994 logf("nothing to commit");
1947 close(tmpfd); 1995 close(tmpfd);
@@ -1987,11 +2035,11 @@ static bool commit(void)
1987 return false; 2035 return false;
1988 } 2036 }
1989 2037
1990 logf("commit %d entries...", header.entry_count); 2038 logf("commit %d entries...", tch.entry_count);
1991 2039
1992 /* Now create the index files. */ 2040 /* Now create the index files. */
1993 stat.commit_step = 0; 2041 stat.commit_step = 0;
1994 header.datasize = 0; 2042 tch.datasize = 0;
1995 stat.commit_delayed = false; 2043 stat.commit_delayed = false;
1996 2044
1997 for (i = 0; i < TAG_COUNT; i++) 2045 for (i = 0; i < TAG_COUNT; i++)
@@ -2001,11 +2049,11 @@ static bool commit(void)
2001 stat.commit_step++; 2049 stat.commit_step++;
2002 if (tagcache_is_numeric_tag(i)) 2050 if (tagcache_is_numeric_tag(i))
2003 { 2051 {
2004 build_numeric_index(i, &header, tmpfd); 2052 build_numeric_index(i, &tch, tmpfd);
2005 continue; 2053 continue;
2006 } 2054 }
2007 2055
2008 ret = build_index(i, &header, tmpfd); 2056 ret = build_index(i, &tch, tmpfd);
2009 if (ret <= 0) 2057 if (ret <= 0)
2010 { 2058 {
2011 close(tmpfd); 2059 close(tmpfd);
@@ -2023,33 +2071,21 @@ static bool commit(void)
2023 stat.commit_step = 0; 2071 stat.commit_step = 0;
2024 2072
2025 /* Update the master index headers. */ 2073 /* Update the master index headers. */
2026 masterfd = open(TAGCACHE_FILE_MASTER, O_RDWR); 2074 if ( (masterfd = open_master_fd(&tcmh, true)) < 0)
2027 if (masterfd < 0)
2028 {
2029 logf("failed to open master index");
2030 return false; 2075 return false;
2031 }
2032 2076
2033 if (read(masterfd, &header_old, sizeof(struct tagcache_header)) 2077 tcmh.tch.entry_count += tch.entry_count;
2034 != sizeof(struct tagcache_header) || 2078 tcmh.tch.datasize = sizeof(struct master_header)
2035 header_old.magic != TAGCACHE_MAGIC) 2079 + sizeof(struct index_entry) * tcmh.tch.entry_count
2036 { 2080 + tch.datasize;
2037 logf("incorrect header");
2038 close(masterfd);
2039 remove_files();
2040 return false;
2041 }
2042
2043 header.entry_count += header_old.entry_count;
2044 /* Datasize has been recalculated. */
2045 // header.datasize += header_old.datasize;
2046 2081
2047 lseek(masterfd, 0, SEEK_SET); 2082 lseek(masterfd, 0, SEEK_SET);
2048 write(masterfd, &header, sizeof(struct tagcache_header)); 2083 write(masterfd, &tcmh, sizeof(struct master_header));
2049 close(masterfd); 2084 close(masterfd);
2050 2085
2051 logf("tagcache committed"); 2086 logf("tagcache committed");
2052 remove(TAGCACHE_FILE_TEMP); 2087 remove(TAGCACHE_FILE_TEMP);
2088 stat.ready = true;
2053 2089
2054 if (local_allocation) 2090 if (local_allocation)
2055 { 2091 {
@@ -2090,27 +2126,95 @@ static void free_tempbuf(void)
2090 tempbuf_size = 0; 2126 tempbuf_size = 0;
2091} 2127}
2092 2128
2093static int open_master_fd(struct tagcache_header *hdr) 2129static bool update_current_serial(long serial)
2094{ 2130{
2131 struct master_header myhdr;
2095 int fd; 2132 int fd;
2096 2133
2097 fd = open(TAGCACHE_FILE_MASTER, O_RDWR); 2134 if ( (fd = open_master_fd(&myhdr, true)) < 0)
2098 if (fd < 0) 2135 return false;
2136
2137 myhdr.serial = serial;
2138 current_serial = serial;
2139
2140#ifdef HAVE_TC_RAMCACHE
2141 if (hdr)
2142 hdr->h.serial = serial;
2143#endif
2144
2145 /* Write it back */
2146 lseek(fd, 0, SEEK_SET);
2147 write(fd, &myhdr, sizeof(struct master_header));
2148 close(fd);
2149
2150 return true;
2151}
2152
2153long tagcache_increase_serial(void)
2154{
2155 if (!update_current_serial(current_serial + 1))
2156 return -1;
2157
2158 return current_serial;
2159}
2160
2161long tagcache_get_serial(void)
2162{
2163 return current_serial;
2164}
2165
2166static bool modify_numeric_entry(int masterfd, int idx_id, int tag, long data)
2167{
2168 struct index_entry idx;
2169
2170 if (!tagcache_is_numeric_tag(tag))
2171 return false;
2172
2173#ifdef HAVE_TC_RAMCACHE
2174 /* Update ram entries first. */
2175 if (hdr)
2099 { 2176 {
2100 logf("master file open failed for R/W"); 2177 hdr->indices[idx_id].tag_seek[tag] = data;
2101 return fd; 2178 hdr->indices[idx_id].flag |= FLAG_DIRTYNUM;
2102 } 2179 }
2180#endif
2103 2181
2104 /* Check the header. */ 2182 /* And now update the db on disk also. */
2105 read(fd, hdr, sizeof(struct tagcache_header)); 2183 lseek(masterfd, idx_id * sizeof(struct index_entry)
2106 if (hdr->magic != TAGCACHE_MAGIC) 2184 + sizeof(struct master_header), SEEK_SET);
2185 if (read(masterfd, &idx, sizeof(struct index_entry))
2186 != sizeof(struct index_entry))
2107 { 2187 {
2108 logf("header error"); 2188 logf("read error");
2109 close(fd); 2189 return false;
2110 return -2;
2111 } 2190 }
2112 2191
2113 return fd; 2192 idx.flag |= FLAG_DIRTYNUM;
2193 idx.tag_seek[tag] = data;
2194
2195 lseek(masterfd, -sizeof(struct index_entry), SEEK_CUR);
2196 if (write(masterfd, &idx, sizeof(struct index_entry))
2197 != sizeof(struct index_entry))
2198 {
2199 logf("write error");
2200 return false;
2201 }
2202
2203 return true;
2204}
2205
2206bool tagcache_modify_numeric_entry(struct tagcache_search *tcs,
2207 int tag, long data)
2208{
2209 struct master_header myhdr;
2210
2211 if (tcs->masterfd < 0)
2212 {
2213 if ( (tcs->masterfd = open_master_fd(&myhdr, true)) < 0)
2214 return false;
2215 }
2216
2217 return modify_numeric_entry(tcs->masterfd, tcs->idx_id, tag, data);
2114} 2218}
2115 2219
2116static bool write_tag(int fd, const char *tagstr, const char *datastr) 2220static 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)
2141 return true; 2245 return true;
2142} 2246}
2143 2247
2248static bool read_tag(char *dest, long size,
2249 const char *src, const char *tagstr)
2250{
2251 int pos;
2252 char current_tag[32];
2253
2254 while (*src != '\0')
2255 {
2256 /* Skip all whitespace */
2257 while (*src == ' ')
2258 src++;
2259
2260 if (*src == '\0')
2261 break;
2262
2263 pos = 0;
2264 /* Read in tag name */
2265 while (*src != '=' && *src != ' ')
2266 {
2267 current_tag[pos] = *src;
2268 src++;
2269 pos++;
2270
2271 if (*src == '\0' || pos >= (long)sizeof(current_tag))
2272 return false;
2273 }
2274 current_tag[pos] = '\0';
2275
2276 /* Read in tag data */
2277
2278 /* Find the start. */
2279 while (*src != '"' && *src != '\0')
2280 src++;
2281
2282 if (*src == '\0' || *(++src) == '\0')
2283 return false;
2284
2285 /* Read the data. */
2286 for (pos = 0; pos < size; pos++)
2287 {
2288 if (*src == '\0')
2289 break;
2290
2291 if (*src == '\\' && *(src+1) == '"')
2292 {
2293 dest[pos] = '"';
2294 src += 2;
2295 continue;
2296 }
2297
2298 dest[pos] = *src;
2299
2300 if (*src == '"')
2301 {
2302 src++;
2303 break;
2304 }
2305
2306 if (*src == '\0')
2307 break;
2308
2309 src++;
2310 }
2311 dest[pos] = '\0';
2312
2313 if (!strcasecmp(tagstr, current_tag))
2314 return true;
2315 }
2316
2317 return false;
2318}
2319
2320static bool parse_changelog_line(int masterfd, const char *buf)
2321{
2322 char tag_data[MAX_PATH];
2323 int idx_id;
2324 const int import_tags[] = { tag_playcount, tag_playtime, tag_lastplayed };
2325 int i;
2326
2327 if (*buf == '#')
2328 return true;
2329
2330 if (!read_tag(tag_data, sizeof tag_data, buf, "filename"))
2331 {
2332 logf("filename missing");
2333 logf("-> %s", buf);
2334 return false;
2335 }
2336
2337 idx_id = find_index(tag_data);
2338 if (idx_id < 0)
2339 {
2340 logf("entry not found");
2341 return false;
2342 }
2343
2344 for (i = 0; i < (long)(sizeof(import_tags)/sizeof(import_tags[0])); i++)
2345 {
2346 int data;
2347
2348 if (!read_tag(tag_data, sizeof tag_data, buf,
2349 tagcache_tag_to_str(import_tags[i])))
2350 {
2351 continue;
2352 }
2353
2354 data = atoi(tag_data);
2355 if (data < 0)
2356 continue;
2357
2358 modify_numeric_entry(masterfd, idx_id, import_tags[i], data);
2359
2360 if (import_tags[i] == tag_lastplayed && data > current_serial)
2361 current_serial = data;
2362 }
2363
2364 return true;
2365}
2366
2367bool tagcache_import_changelog(void)
2368{
2369 struct master_header myhdr;
2370 int clfd, masterfd;
2371 char buf[512];
2372 int pos = 0;
2373
2374 clfd = open(TAGCACHE_FILE_CHANGELOG, O_RDONLY);
2375 if (clfd < 0)
2376 {
2377 logf("failure to open changelog");
2378 return false;
2379 }
2380
2381 if ( (masterfd = open_master_fd(&myhdr, true)) < 0)
2382 {
2383 close(clfd);
2384 return false;
2385 }
2386
2387 /* Fast readline */
2388 while ( 1 )
2389 {
2390 char *p;
2391 char *next = NULL;
2392 int rc;
2393
2394 rc = read(clfd, &buf[pos], sizeof(buf)-pos-1);
2395 if (rc >= 0)
2396 buf[pos+rc] = '\0';
2397
2398 if ( (p = strchr(buf, '\r')) != NULL)
2399 {
2400 *p = '\0';
2401 next = ++p;
2402 }
2403 else
2404 p = buf;
2405
2406 if ( (p = strchr(p, '\n')) != NULL)
2407 {
2408 *p = '\0';
2409 next = ++p;
2410 }
2411
2412 parse_changelog_line(masterfd, buf);
2413
2414 if (next)
2415 {
2416 pos = sizeof(buf) - ((long)next - (long)buf) - 1;
2417 memmove(buf, next, pos);
2418 }
2419 else
2420 break ;
2421 }
2422
2423 close(clfd);
2424 close(masterfd);
2425
2426 update_current_serial(current_serial);
2427
2428 return true;
2429}
2430
2144bool tagcache_create_changelog(struct tagcache_search *tcs) 2431bool tagcache_create_changelog(struct tagcache_search *tcs)
2145{ 2432{
2146 static const char *tags_str[] = { "artist", "album", "genre", "title", 2433 struct master_header myhdr;
2147 "filename", "playcount", "playtime", "lastplayed" };
2148 static const int tags[] = { tag_artist, tag_album, tag_genre, tag_title,
2149 tag_filename, tag_playcount, tag_playtime, tag_lastplayed };
2150 struct tagcache_header myhdr;
2151 struct index_entry idx; 2434 struct index_entry idx;
2152 char buf[256]; 2435 char buf[256];
2153 char temp[32]; 2436 char temp[32];
@@ -2167,18 +2450,18 @@ bool tagcache_create_changelog(struct tagcache_search *tcs)
2167 2450
2168 if (tcs->masterfd < 0) 2451 if (tcs->masterfd < 0)
2169 { 2452 {
2170 if ( (tcs->masterfd = open_master_fd(&myhdr)) < 0) 2453 if ( (tcs->masterfd = open_master_fd(&myhdr, false)) < 0)
2171 return false; 2454 return false;
2172 } 2455 }
2173 else 2456 else
2174 { 2457 {
2175 lseek(tcs->masterfd, 0, SEEK_SET); 2458 lseek(tcs->masterfd, 0, SEEK_SET);
2176 read(tcs->masterfd, &myhdr, sizeof(struct tagcache_header)); 2459 read(tcs->masterfd, &myhdr, sizeof(struct master_header));
2177 } 2460 }
2178 2461
2179 write(clfd, "## Changelog version 1\n", 23); 2462 write(clfd, "## Changelog version 1\n", 23);
2180 2463
2181 for (i = 0; i < myhdr.entry_count; i++) 2464 for (i = 0; i < myhdr.tch.entry_count; i++)
2182 { 2465 {
2183 if (read(tcs->masterfd, &idx, sizeof(struct index_entry)) 2466 if (read(tcs->masterfd, &idx, sizeof(struct index_entry))
2184 != sizeof(struct index_entry)) 2467 != sizeof(struct index_entry))
@@ -2196,19 +2479,19 @@ bool tagcache_create_changelog(struct tagcache_search *tcs)
2196 logf("Found!"); 2479 logf("Found!");
2197 2480
2198 /* Now retrieve all tags. */ 2481 /* Now retrieve all tags. */
2199 for (j = 0; j < (long)(sizeof(tags) / sizeof(tags[0])); j++) 2482 for (j = 0; j < TAG_COUNT; j++)
2200 { 2483 {
2201 if (tagcache_is_numeric_tag(tags[j])) 2484 if (tagcache_is_numeric_tag(j))
2202 { 2485 {
2203 snprintf(temp, sizeof temp, "%d", idx.tag_seek[tags[j]]); 2486 snprintf(temp, sizeof temp, "%d", idx.tag_seek[j]);
2204 write_tag(clfd, tags_str[j], temp); 2487 write_tag(clfd, tagcache_tag_to_str(j), temp);
2205 continue; 2488 continue;
2206 } 2489 }
2207 2490
2208 tcs->type = tags[j]; 2491 tcs->type = j;
2209 tagcache_retrieve(tcs, i, buf, sizeof buf); 2492 tagcache_retrieve(tcs, i, buf, sizeof buf);
2210 logf("tag: %s", buf); 2493 logf("tag: %s", buf);
2211 write_tag(clfd, tags_str[j], buf); 2494 write_tag(clfd, tagcache_tag_to_str(j), buf);
2212 } 2495 }
2213 2496
2214 write(clfd, "\n", 1); 2497 write(clfd, "\n", 1);
@@ -2221,60 +2504,12 @@ bool tagcache_create_changelog(struct tagcache_search *tcs)
2221 return true; 2504 return true;
2222} 2505}
2223 2506
2224bool tagcache_modify_numeric_entry(struct tagcache_search *tcs,
2225 int tag, long data)
2226{
2227 struct index_entry idx;
2228 struct tagcache_header myhdr;
2229
2230 if (!tagcache_is_numeric_tag(tag))
2231 return false;
2232
2233#ifdef HAVE_TC_RAMCACHE
2234 /* Update ram entries first. */
2235 if (tcs->ramsearch)
2236 {
2237 hdr->indices[tcs->idx_id].tag_seek[tag] = data;
2238 hdr->indices[tcs->idx_id].flag |= FLAG_DIRTYNUM;
2239 }
2240#endif
2241
2242 /* And now update the db on disk also. */
2243 if (tcs->masterfd < 0)
2244 {
2245 if ( (tcs->masterfd = open_master_fd(&myhdr)) < 0)
2246 return false;
2247 }
2248
2249 lseek(tcs->masterfd, tcs->idx_id * sizeof(struct index_entry)
2250 + sizeof(struct tagcache_header), SEEK_SET);
2251 if (read(tcs->masterfd, &idx, sizeof(struct index_entry))
2252 != sizeof(struct index_entry))
2253 {
2254 logf("read error");
2255 return false;
2256 }
2257
2258 idx.flag |= FLAG_DIRTYNUM;
2259 idx.tag_seek[tag] = data;
2260
2261 lseek(tcs->masterfd, -sizeof(struct index_entry), SEEK_CUR);
2262 if (write(tcs->masterfd, &idx, sizeof(struct index_entry))
2263 != sizeof(struct index_entry))
2264 {
2265 logf("write error");
2266 return false;
2267 }
2268
2269 return true;
2270}
2271
2272static bool delete_entry(long idx_id) 2507static bool delete_entry(long idx_id)
2273{ 2508{
2274 int fd; 2509 int fd;
2275 int tag, i; 2510 int tag, i;
2276 struct index_entry idx, myidx; 2511 struct index_entry idx, myidx;
2277 struct tagcache_header myhdr; 2512 struct master_header myhdr;
2278 char buf[MAX_PATH]; 2513 char buf[MAX_PATH];
2279 int in_use[TAG_COUNT]; 2514 int in_use[TAG_COUNT];
2280 2515
@@ -2284,7 +2519,7 @@ static bool delete_entry(long idx_id)
2284 hdr->indices[idx_id].flag |= FLAG_DELETED; 2519 hdr->indices[idx_id].flag |= FLAG_DELETED;
2285#endif 2520#endif
2286 2521
2287 if ( (fd = open_master_fd(&myhdr) < 0) ) 2522 if ( (fd = open_master_fd(&myhdr, true) < 0) )
2288 return false; 2523 return false;
2289 2524
2290 lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR); 2525 lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR);
@@ -2310,8 +2545,8 @@ static bool delete_entry(long idx_id)
2310 for (tag = 0; tag < TAG_COUNT; tag++) 2545 for (tag = 0; tag < TAG_COUNT; tag++)
2311 in_use[tag] = 0; 2546 in_use[tag] = 0;
2312 2547
2313 lseek(fd, sizeof(struct tagcache_header), SEEK_SET); 2548 lseek(fd, sizeof(struct master_header), SEEK_SET);
2314 for (i = 0; i < myhdr.entry_count; i++) 2549 for (i = 0; i < myhdr.tch.entry_count; i++)
2315 { 2550 {
2316 if (read(fd, &idx, sizeof(struct index_entry)) 2551 if (read(fd, &idx, sizeof(struct index_entry))
2317 != sizeof(struct index_entry)) 2552 != sizeof(struct index_entry))
@@ -2394,11 +2629,11 @@ static bool allocate_tagcache(void)
2394 /* Load the header. */ 2629 /* Load the header. */
2395 hdr = (struct ramcache_header *)(((long)audiobuf & ~0x03) + 0x04); 2630 hdr = (struct ramcache_header *)(((long)audiobuf & ~0x03) + 0x04);
2396 memset(hdr, 0, sizeof(struct ramcache_header)); 2631 memset(hdr, 0, sizeof(struct ramcache_header));
2397 len = sizeof(struct tagcache_header); 2632 len = sizeof(struct master_header);
2398 rc = read(fd, &hdr->h, len); 2633 rc = read(fd, &hdr->h, len);
2399 close(fd); 2634 close(fd);
2400 2635
2401 if (hdr->h.magic != TAGCACHE_MAGIC || rc != len) 2636 if (hdr->h.tch.magic != TAGCACHE_MAGIC || rc != len)
2402 { 2637 {
2403 logf("incorrect header"); 2638 logf("incorrect header");
2404 remove_files(); 2639 remove_files();
@@ -2412,8 +2647,7 @@ static bool allocate_tagcache(void)
2412 * Now calculate the required cache size plus 2647 * Now calculate the required cache size plus
2413 * some extra space for alignment fixes. 2648 * some extra space for alignment fixes.
2414 */ 2649 */
2415 stat.ramcache_allocated = hdr->h.datasize + 128 + TAGCACHE_RESERVE + 2650 stat.ramcache_allocated = hdr->h.tch.datasize + 128 + TAGCACHE_RESERVE +
2416 sizeof(struct index_entry) * hdr->h.entry_count +
2417 sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *); 2651 sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *);
2418 logf("tagcache: %d bytes allocated.", stat.ramcache_allocated); 2652 logf("tagcache: %d bytes allocated.", stat.ramcache_allocated);
2419 logf("at: 0x%04x", audiobuf); 2653 logf("at: 0x%04x", audiobuf);
@@ -2445,20 +2679,18 @@ static bool load_tagcache(void)
2445 return false; 2679 return false;
2446 } 2680 }
2447 2681
2448 if (read(fd, &hdr->h, sizeof(struct tagcache_header)) 2682 if (read(fd, &hdr->h, sizeof(struct master_header))
2449 != sizeof(struct tagcache_header) 2683 != sizeof(struct master_header)
2450 || hdr->h.magic != TAGCACHE_MAGIC) 2684 || hdr->h.tch.magic != TAGCACHE_MAGIC)
2451 { 2685 {
2452 logf("incorrect header"); 2686 logf("incorrect header");
2453 return false; 2687 return false;
2454 } 2688 }
2455 2689
2456 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
2457
2458 idx = hdr->indices; 2690 idx = hdr->indices;
2459 2691
2460 /* Load the master index table. */ 2692 /* Load the master index table. */
2461 for (i = 0; i < hdr->h.entry_count; i++) 2693 for (i = 0; i < hdr->h.tch.entry_count; i++)
2462 { 2694 {
2463 rc = read(fd, idx, sizeof(struct index_entry)); 2695 rc = read(fd, idx, sizeof(struct index_entry));
2464 if (rc != sizeof(struct index_entry)) 2696 if (rc != sizeof(struct index_entry))
@@ -2865,6 +3097,20 @@ static void load_ramcache(void)
2865} 3097}
2866#endif 3098#endif
2867 3099
3100static bool check_master_fd(void)
3101{
3102 struct master_header myhdr;
3103 int fd;
3104
3105 if ( (fd = open_master_fd(&myhdr, false)) < 0)
3106 return false;
3107
3108 close(fd);
3109 current_serial = myhdr.serial;
3110
3111 return true;
3112}
3113
2868static void tagcache_thread(void) 3114static void tagcache_thread(void)
2869{ 3115{
2870 struct event ev; 3116 struct event ev;
@@ -2885,6 +3131,8 @@ static void tagcache_thread(void)
2885 3131
2886 cpu_boost(false); 3132 cpu_boost(false);
2887 3133
3134 stat.ready = check_master_fd();
3135
2888 stat.initialized = true; 3136 stat.initialized = true;
2889 3137
2890 while (1) 3138 while (1)
@@ -2964,7 +3212,7 @@ static int get_progress(void)
2964#ifdef HAVE_TC_RAMCACHE 3212#ifdef HAVE_TC_RAMCACHE
2965 { 3213 {
2966 if (hdr && stat.ramcache) 3214 if (hdr && stat.ramcache)
2967 total_count = hdr->h.entry_count; 3215 total_count = hdr->h.tch.entry_count;
2968 } 3216 }
2969#endif 3217#endif
2970 3218
@@ -2989,6 +3237,9 @@ void tagcache_start_scan(void)
2989 3237
2990bool tagcache_update(void) 3238bool tagcache_update(void)
2991{ 3239{
3240 if (!stat.ready)
3241 return false;
3242
2992 queue_post(&tagcache_queue, Q_UPDATE, 0); 3243 queue_post(&tagcache_queue, Q_UPDATE, 0);
2993 gui_syncsplash(HZ*2, true, str(LANG_TAGCACHE_FORCE_UPDATE_SPLASH)); 3244 gui_syncsplash(HZ*2, true, str(LANG_TAGCACHE_FORCE_UPDATE_SPLASH));
2994 3245
@@ -3019,8 +3270,10 @@ bool tagcache_is_ramcache(void)
3019void tagcache_init(void) 3270void tagcache_init(void)
3020{ 3271{
3021 stat.initialized = false; 3272 stat.initialized = false;
3273 stat.ready = false;
3022 stat.commit_step = 0; 3274 stat.commit_step = 0;
3023 filenametag_fd = -1; 3275 filenametag_fd = -1;
3276 current_serial = 0;
3024 3277
3025 queue_init(&tagcache_queue); 3278 queue_init(&tagcache_queue);
3026 create_thread(tagcache_thread, tagcache_stack, 3279 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,
36#define IDX_BUF_DEPTH 64 36#define IDX_BUF_DEPTH 64
37 37
38/* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ 38/* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */
39#define TAGCACHE_MAGIC 0x54434805 39#define TAGCACHE_MAGIC 0x54434806
40 40
41/* How much to allocate extra space for ramcache. */ 41/* How much to allocate extra space for ramcache. */
42#define TAGCACHE_RESERVE 32768 42#define TAGCACHE_RESERVE 32768
@@ -79,6 +79,7 @@ enum modifiers { clause_mod_none, clause_mod_not };
79 79
80struct tagcache_stat { 80struct tagcache_stat {
81 bool initialized; /* Is tagcache currently busy? */ 81 bool initialized; /* Is tagcache currently busy? */
82 bool ready; /* Is tagcache ready to be used? */
82 bool ramcache; /* Is tagcache loaded in ram? */ 83 bool ramcache; /* Is tagcache loaded in ram? */
83 bool commit_delayed; /* Has commit been delayed until next reboot? */ 84 bool commit_delayed; /* Has commit been delayed until next reboot? */
84 int commit_step; /* Commit progress */ 85 int commit_step; /* Commit progress */
@@ -124,6 +125,9 @@ struct tagcache_search {
124 long result_seek; 125 long result_seek;
125}; 126};
126 127
128int tagcache_str_to_tag(const char *str);
129const char* tagcache_tag_to_str(int tag);
130
127bool tagcache_is_numeric_tag(int type); 131bool tagcache_is_numeric_tag(int type);
128bool tagcache_is_unique_tag(int type); 132bool tagcache_is_unique_tag(int type);
129bool tagcache_is_sorted_tag(int type); 133bool tagcache_is_sorted_tag(int type);
@@ -138,6 +142,9 @@ bool tagcache_retrieve(struct tagcache_search *tcs, int idxid,
138 char *buf, long size); 142 char *buf, long size);
139void tagcache_search_finish(struct tagcache_search *tcs); 143void tagcache_search_finish(struct tagcache_search *tcs);
140long tagcache_get_numeric(const struct tagcache_search *tcs, int tag); 144long tagcache_get_numeric(const struct tagcache_search *tcs, int tag);
145long tagcache_increase_serial(void);
146long tagcache_get_serial(void);
147bool tagcache_import_changelog(void);
141bool tagcache_create_changelog(struct tagcache_search *tcs); 148bool tagcache_create_changelog(struct tagcache_search *tcs);
142bool tagcache_modify_numeric_entry(struct tagcache_search *tcs, 149bool tagcache_modify_numeric_entry(struct tagcache_search *tcs,
143 int tag, long data); 150 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)
395 395
396 playcount++; 396 playcount++;
397 397
398 lastplayed = tagcache_increase_serial();
399 if (lastplayed < 0)
400 return;
401
398 /* Ignore the last 15s (crossfade etc.) */ 402 /* Ignore the last 15s (crossfade etc.) */
399 playtime += MIN(id3->length, id3->elapsed + 15 * 1000); 403 playtime += MIN(id3->length, id3->elapsed + 15 * 1000);
400 404
@@ -427,6 +431,17 @@ bool tagtree_export(void)
427 return false; 431 return false;
428} 432}
429 433
434bool tagtree_import(void)
435{
436 gui_syncsplash(0, true, str(LANG_WAIT));
437 if (!tagcache_import_changelog())
438 {
439 gui_syncsplash(HZ*2, true, str(LANG_FAILED));
440 }
441
442 return false;
443}
444
430void tagtree_init(void) 445void tagtree_init(void)
431{ 446{
432 int fd; 447 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 {
31}; 31};
32 32
33bool tagtree_export(void); 33bool tagtree_export(void);
34bool tagtree_import(void);
34void tagtree_init(void); 35void tagtree_init(void);
35int tagtree_enter(struct tree_context* c); 36int tagtree_enter(struct tree_context* c);
36void tagtree_exit(struct tree_context* c); 37void tagtree_exit(struct tree_context* c);