summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/tagcache.c195
-rw-r--r--apps/tagcache.h19
2 files changed, 147 insertions, 67 deletions
diff --git a/apps/tagcache.c b/apps/tagcache.c
index bf2df7cc0b..1f4521168e 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -85,7 +85,6 @@
85#define do_timed_yield() do { } while(0) 85#define do_timed_yield() do { } while(0)
86#endif 86#endif
87 87
88
89#ifndef __PCTOOL__ 88#ifndef __PCTOOL__
90/* Tag Cache thread. */ 89/* Tag Cache thread. */
91static struct event_queue tagcache_queue; 90static struct event_queue tagcache_queue;
@@ -115,16 +114,18 @@ static const int unique_tags[] = { tag_artist, tag_album, tag_genre,
115 tag_composer, tag_comment, tag_albumartist, tag_grouping }; 114 tag_composer, tag_comment, tag_albumartist, tag_grouping };
116 115
117/* Numeric tags (we can use these tags with conditional clauses). */ 116/* Numeric tags (we can use these tags with conditional clauses). */
118static const int numeric_tags[] = { tag_year, tag_discnumber, tag_tracknumber, tag_length, 117static const int numeric_tags[] = { tag_year, tag_discnumber,
119 tag_bitrate, tag_playcount, tag_rating, tag_playtime, tag_lastplayed, tag_commitid, 118 tag_tracknumber, tag_length, tag_bitrate, tag_playcount, tag_rating,
119 tag_playtime, tag_lastplayed, tag_commitid, tag_mtime,
120 tag_virt_length_min, tag_virt_length_sec, 120 tag_virt_length_min, tag_virt_length_sec,
121 tag_virt_playtime_min, tag_virt_playtime_sec, 121 tag_virt_playtime_min, tag_virt_playtime_sec,
122 tag_virt_entryage, tag_virt_autoscore }; 122 tag_virt_entryage, tag_virt_autoscore };
123 123
124/* String presentation of the tags defined in tagcache.h. Must be in correct order! */ 124/* String presentation of the tags defined in tagcache.h. Must be in correct order! */
125static const char *tags_str[] = { "artist", "album", "genre", "title", 125static const char *tags_str[] = { "artist", "album", "genre", "title",
126 "filename", "composer", "comment", "albumartist", "grouping", "year", "discnumber", "tracknumber", 126 "filename", "composer", "comment", "albumartist", "grouping", "year",
127 "bitrate", "length", "playcount", "rating", "playtime", "lastplayed", "commitid" }; 127 "discnumber", "tracknumber", "bitrate", "length", "playcount", "rating",
128 "playtime", "lastplayed", "commitid", "mtime" };
128 129
129/* Status information of the tagcache. */ 130/* Status information of the tagcache. */
130static struct tagcache_stat tc_stat; 131static struct tagcache_stat tc_stat;
@@ -188,7 +189,7 @@ struct master_header {
188 189
189/* For the endianess correction */ 190/* For the endianess correction */
190static const char *tagfile_entry_ec = "ss"; 191static const char *tagfile_entry_ec = "ss";
191static const char *index_entry_ec = "llllllllllllllllllll"; /* (1 + TAG_COUNT) * l */ 192static const char *index_entry_ec = "lllllllllllllllllllll"; /* (1 + TAG_COUNT) * l */
192static const char *tagcache_header_ec = "lll"; 193static const char *tagcache_header_ec = "lll";
193static const char *master_header_ec = "llllll"; 194static const char *master_header_ec = "llllll";
194 195
@@ -252,6 +253,8 @@ static int processed_dir_count;
252static volatile int write_lock; 253static volatile int write_lock;
253static volatile int read_lock; 254static volatile int read_lock;
254 255
256static bool delete_entry(long idx_id);
257
255const char* tagcache_tag_to_str(int tag) 258const char* tagcache_tag_to_str(int tag)
256{ 259{
257 return tags_str[tag]; 260 return tags_str[tag];
@@ -328,6 +331,46 @@ static int open_tag_fd(struct tagcache_header *hdr, int tag, bool write)
328 return fd; 331 return fd;
329} 332}
330 333
334static int open_master_fd(struct master_header *hdr, bool write)
335{
336 int fd;
337 int rc;
338
339 fd = open(TAGCACHE_FILE_MASTER, write ? O_RDWR : O_RDONLY);
340 if (fd < 0)
341 {
342 logf("master file open failed for R/W");
343 tc_stat.ready = false;
344 return fd;
345 }
346
347 tc_stat.econ = false;
348
349 /* Check the header. */
350 rc = read(fd, hdr, sizeof(struct master_header));
351 if (hdr->tch.magic == TAGCACHE_MAGIC && rc == sizeof(struct master_header))
352 {
353 /* Success. */
354 return fd;
355 }
356
357 /* Trying to read again, this time with endianess correction enabled. */
358 lseek(fd, 0, SEEK_SET);
359
360 rc = ecread(fd, hdr, 1, master_header_ec, true);
361 if (hdr->tch.magic != TAGCACHE_MAGIC || rc != sizeof(struct master_header))
362 {
363 logf("header error");
364 tc_stat.ready = false;
365 close(fd);
366 return -2;
367 }
368
369 tc_stat.econ = true;
370
371 return fd;
372}
373
331#ifndef __PCTOOL__ 374#ifndef __PCTOOL__
332static bool do_timed_yield(void) 375static bool do_timed_yield(void)
333{ 376{
@@ -521,6 +564,8 @@ bool tagcache_find_index(struct tagcache_search *tcs, const char *filename)
521static bool get_index(int masterfd, int idxid, 564static bool get_index(int masterfd, int idxid,
522 struct index_entry *idx, bool use_ram) 565 struct index_entry *idx, bool use_ram)
523{ 566{
567 bool localfd = false;
568
524 if (idxid < 0) 569 if (idxid < 0)
525 { 570 {
526 logf("Incorrect idxid: %d", idxid); 571 logf("Incorrect idxid: %d", idxid);
@@ -540,15 +585,31 @@ static bool get_index(int masterfd, int idxid,
540 (void)use_ram; 585 (void)use_ram;
541#endif 586#endif
542 587
588 if (masterfd < 0)
589 {
590 struct master_header tcmh;
591
592 localfd = true;
593 masterfd = open_master_fd(&tcmh, false);
594 if (masterfd < 0)
595 return false;
596 }
597
543 lseek(masterfd, idxid * sizeof(struct index_entry) 598 lseek(masterfd, idxid * sizeof(struct index_entry)
544 + sizeof(struct master_header), SEEK_SET); 599 + sizeof(struct master_header), SEEK_SET);
545 if (ecread(masterfd, idx, 1, index_entry_ec, tc_stat.econ) 600 if (ecread(masterfd, idx, 1, index_entry_ec, tc_stat.econ)
546 != sizeof(struct index_entry)) 601 != sizeof(struct index_entry))
547 { 602 {
548 logf("read error #3"); 603 logf("read error #3");
604 if (localfd)
605 close(masterfd);
606
549 return false; 607 return false;
550 } 608 }
551 609
610 if (localfd)
611 close(masterfd);
612
552 if (idx->flag & FLAG_DELETED) 613 if (idx->flag & FLAG_DELETED)
553 return false; 614 return false;
554 615
@@ -1080,46 +1141,6 @@ static void remove_files(void)
1080} 1141}
1081 1142
1082 1143
1083static int open_master_fd(struct master_header *hdr, bool write)
1084{
1085 int fd;
1086 int rc;
1087
1088 fd = open(TAGCACHE_FILE_MASTER, write ? O_RDWR : O_RDONLY);
1089 if (fd < 0)
1090 {
1091 logf("master file open failed for R/W");
1092 tc_stat.ready = false;
1093 return fd;
1094 }
1095
1096 tc_stat.econ = false;
1097
1098 /* Check the header. */
1099 rc = read(fd, hdr, sizeof(struct master_header));
1100 if (hdr->tch.magic == TAGCACHE_MAGIC && rc == sizeof(struct master_header))
1101 {
1102 /* Success. */
1103 return fd;
1104 }
1105
1106 /* Trying to read again, this time with endianess correction enabled. */
1107 lseek(fd, 0, SEEK_SET);
1108
1109 rc = ecread(fd, hdr, 1, master_header_ec, true);
1110 if (hdr->tch.magic != TAGCACHE_MAGIC || rc != sizeof(struct master_header))
1111 {
1112 logf("header error");
1113 tc_stat.ready = false;
1114 close(fd);
1115 return -2;
1116 }
1117
1118 tc_stat.econ = true;
1119
1120 return fd;
1121}
1122
1123static bool check_all_headers(void) 1144static bool check_all_headers(void)
1124{ 1145{
1125 struct master_header myhdr; 1146 struct master_header myhdr;
@@ -1602,16 +1623,17 @@ static int check_if_empty(char **tag)
1602 entry.tag_length[tag] = check_if_empty(data); \ 1623 entry.tag_length[tag] = check_if_empty(data); \
1603 offset += entry.tag_length[tag] 1624 offset += entry.tag_length[tag]
1604 1625
1626static void add_tagcache(char *path, unsigned long mtime
1605#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) 1627#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1606static void add_tagcache(char *path, const struct dirent *dc) 1628 ,const struct dirent *dc
1607#else
1608static void add_tagcache(char *path)
1609#endif 1629#endif
1630 )
1610{ 1631{
1611 struct mp3entry id3; 1632 struct mp3entry id3;
1612 struct temp_file_entry entry; 1633 struct temp_file_entry entry;
1613 bool ret; 1634 bool ret;
1614 int fd; 1635 int fd;
1636 int idx_id = -1;
1615 char tracknumfix[3]; 1637 char tracknumfix[3];
1616 int offset = 0; 1638 int offset = 0;
1617 int path_length = strlen(path); 1639 int path_length = strlen(path);
@@ -1637,16 +1659,40 @@ static void add_tagcache(char *path)
1637#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) 1659#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1638 if (tc_stat.ramcache && dircache_is_enabled()) 1660 if (tc_stat.ramcache && dircache_is_enabled())
1639 { 1661 {
1640 if (find_entry_ram(path, dc) >= 0) 1662 idx_id = find_entry_ram(path, dc);
1641 return ;
1642 } 1663 }
1643 else 1664 else
1644#endif 1665#endif
1645 { 1666 {
1646 if (filenametag_fd >= 0) 1667 if (filenametag_fd >= 0)
1647 { 1668 {
1648 if (find_entry_disk(path) >= 0) 1669 idx_id = find_entry_disk(path);
1649 return ; 1670 }
1671 }
1672
1673 /* Check if file has been modified. */
1674 if (idx_id >= 0)
1675 {
1676 struct index_entry idx;
1677
1678 if (!get_index(-1, idx_id, &idx, true))
1679 {
1680 logf("failed to retrieve index entry");
1681 return ;
1682 }
1683
1684 if ((unsigned long)idx.tag_seek[tag_mtime] == mtime)
1685 {
1686 /* No changes to file. */
1687 return ;
1688 }
1689
1690 /* Metadata might have been changed. Delete the entry. */
1691 logf("Re-adding: %s", path);
1692 if (!delete_entry(idx_id))
1693 {
1694 logf("delete_entry failed: %d", idx_id);
1695 return ;
1650 } 1696 }
1651 } 1697 }
1652 1698
@@ -1706,6 +1752,7 @@ static void add_tagcache(char *path)
1706 entry.tag_offset[tag_tracknumber] = id3.tracknum; 1752 entry.tag_offset[tag_tracknumber] = id3.tracknum;
1707 entry.tag_offset[tag_length] = id3.length; 1753 entry.tag_offset[tag_length] = id3.length;
1708 entry.tag_offset[tag_bitrate] = id3.bitrate; 1754 entry.tag_offset[tag_bitrate] = id3.bitrate;
1755 entry.tag_offset[tag_mtime] = mtime;
1709 1756
1710 /* String tags. */ 1757 /* String tags. */
1711 has_albumartist = id3.albumartist != NULL 1758 has_albumartist = id3.albumartist != NULL
@@ -3241,7 +3288,8 @@ bool tagcache_create_changelog(struct tagcache_search *tcs)
3241 3288
3242static bool delete_entry(long idx_id) 3289static bool delete_entry(long idx_id)
3243{ 3290{
3244 int fd; 3291 int fd = -1;
3292 /*int dbdel_fd = -1;*/
3245 int tag, i; 3293 int tag, i;
3246 struct index_entry idx, myidx; 3294 struct index_entry idx, myidx;
3247 struct master_header myhdr; 3295 struct master_header myhdr;
@@ -3259,13 +3307,23 @@ static bool delete_entry(long idx_id)
3259 if ( (fd = open_master_fd(&myhdr, true) ) < 0) 3307 if ( (fd = open_master_fd(&myhdr, true) ) < 0)
3260 return false; 3308 return false;
3261 3309
3310 /*
3311 TODO: Implement soon.
3312 dbdel_fd = open(TAGCACHE_FILE_DELETED, O_RDWR | O_APPEND | O_CREAT);
3313 if (dbdel_fd < 0)
3314 {
3315 logf("delete_entry(): DBDEL open failed");
3316 goto cleanup;
3317 }
3318 close(dbdel_fd);
3319 dbdel_fd = -1;
3320 */
3262 lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR); 3321 lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR);
3263 if (ecread(fd, &myidx, 1, index_entry_ec, tc_stat.econ) 3322 if (ecread(fd, &myidx, 1, index_entry_ec, tc_stat.econ)
3264 != sizeof(struct index_entry)) 3323 != sizeof(struct index_entry))
3265 { 3324 {
3266 logf("delete_entry(): read error"); 3325 logf("delete_entry(): read error");
3267 close(fd); 3326 goto cleanup;
3268 return false;
3269 } 3327 }
3270 3328
3271 myidx.flag |= FLAG_DELETED; 3329 myidx.flag |= FLAG_DELETED;
@@ -3274,8 +3332,7 @@ static bool delete_entry(long idx_id)
3274 != sizeof(struct index_entry)) 3332 != sizeof(struct index_entry))
3275 { 3333 {
3276 logf("delete_entry(): write_error"); 3334 logf("delete_entry(): write_error");
3277 close(fd); 3335 goto cleanup;
3278 return false;
3279 } 3336 }
3280 3337
3281 /* Now check which tags are no longer in use (if any) */ 3338 /* Now check which tags are no longer in use (if any) */
@@ -3298,8 +3355,7 @@ static bool delete_entry(long idx_id)
3298 != sizeof(struct index_entry)) 3355 != sizeof(struct index_entry))
3299 { 3356 {
3300 logf("delete_entry(): read error #2"); 3357 logf("delete_entry(): read error #2");
3301 close(fd); 3358 goto cleanup;
3302 return false;
3303 } 3359 }
3304 idxp = &idx; 3360 idxp = &idx;
3305 } 3361 }
@@ -3318,6 +3374,7 @@ static bool delete_entry(long idx_id)
3318 } 3374 }
3319 3375
3320 close(fd); 3376 close(fd);
3377 fd = -1;
3321 3378
3322 /* Now delete all tags no longer in use. */ 3379 /* Now delete all tags no longer in use. */
3323 for (tag = 0; tag < TAG_COUNT; tag++) 3380 for (tag = 0; tag < TAG_COUNT; tag++)
@@ -3347,7 +3404,7 @@ static bool delete_entry(long idx_id)
3347 if (fd < 0) 3404 if (fd < 0)
3348 { 3405 {
3349 logf("open failed"); 3406 logf("open failed");
3350 return false; 3407 goto cleanup;
3351 } 3408 }
3352 3409
3353 /* Skip the header block */ 3410 /* Skip the header block */
@@ -3368,6 +3425,14 @@ static bool delete_entry(long idx_id)
3368 } 3425 }
3369 3426
3370 return true; 3427 return true;
3428
3429 cleanup:
3430 if (fd >= 0)
3431 close(fd);
3432/* if (dbdel_fd >= 0)
3433 close(dbdel_fd);
3434 */
3435 return false;
3371} 3436}
3372 3437
3373#ifndef __PCTOOL__ 3438#ifndef __PCTOOL__
@@ -3846,11 +3911,13 @@ static bool check_dir(const char *dirname)
3846 else 3911 else
3847 { 3912 {
3848 tc_stat.curentry = curpath; 3913 tc_stat.curentry = curpath;
3914
3915 /* Add a new entry to the temporary db file. */
3916 add_tagcache(curpath, (entry->wrtdate << 16) | entry->wrttime
3849#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) 3917#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
3850 add_tagcache(curpath, dir->internal_entry); 3918 , dir->internal_entry
3851#else
3852 add_tagcache(curpath);
3853#endif 3919#endif
3920 );
3854 3921
3855 /* Wait until current path for debug screen is read and unset. */ 3922 /* Wait until current path for debug screen is read and unset. */
3856 while (tc_stat.syncscreen && tc_stat.curentry != NULL) 3923 while (tc_stat.syncscreen && tc_stat.curentry != NULL)
diff --git a/apps/tagcache.h b/apps/tagcache.h
index 39f0c61f7e..879cf66fbd 100644
--- a/apps/tagcache.h
+++ b/apps/tagcache.h
@@ -25,13 +25,13 @@
25enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, 25enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
26 tag_filename, tag_composer, tag_comment, tag_albumartist, tag_grouping, tag_year, 26 tag_filename, tag_composer, tag_comment, tag_albumartist, tag_grouping, tag_year,
27 tag_discnumber, tag_tracknumber, tag_bitrate, tag_length, tag_playcount, tag_rating, 27 tag_discnumber, tag_tracknumber, tag_bitrate, tag_length, tag_playcount, tag_rating,
28 tag_playtime, tag_lastplayed, tag_commitid, 28 tag_playtime, tag_lastplayed, tag_commitid, tag_mtime,
29 /* Virtual tags */ 29 /* Virtual tags */
30 tag_virt_length_min, tag_virt_length_sec, 30 tag_virt_length_min, tag_virt_length_sec,
31 tag_virt_playtime_min, tag_virt_playtime_sec, 31 tag_virt_playtime_min, tag_virt_playtime_sec,
32 tag_virt_entryage, tag_virt_autoscore }; 32 tag_virt_entryage, tag_virt_autoscore };
33 33
34#define TAG_COUNT 19 34#define TAG_COUNT 20
35 35
36/* Maximum length of a single tag. */ 36/* Maximum length of a single tag. */
37#define TAG_MAXLEN (MAX_PATH*2) 37#define TAG_MAXLEN (MAX_PATH*2)
@@ -43,7 +43,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
43#define IDX_BUF_DEPTH 64 43#define IDX_BUF_DEPTH 64
44 44
45/* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ 45/* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */
46#define TAGCACHE_MAGIC 0x5443480b 46#define TAGCACHE_MAGIC 0x5443480c
47 47
48/* How much to allocate extra space for ramcache. */ 48/* How much to allocate extra space for ramcache. */
49#define TAGCACHE_RESERVE 32768 49#define TAGCACHE_RESERVE 32768
@@ -72,10 +72,23 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
72#define TAGCACHE_MAX_CLAUSES 32 72#define TAGCACHE_MAX_CLAUSES 32
73 73
74/* Tag database files. */ 74/* Tag database files. */
75
76/* Temporary database containing new tags to be committed to the main db. */
75#define TAGCACHE_FILE_TEMP ROCKBOX_DIR "/database_tmp.tcd" 77#define TAGCACHE_FILE_TEMP ROCKBOX_DIR "/database_tmp.tcd"
78
79/* Database containing deleted entries with runtime statistics. */
80#define TAGCACHE_FILE_DELETED ROCKBOX_DIR "/database_del.tcd"
81
82/* The main database master index and numeric data. */
76#define TAGCACHE_FILE_MASTER ROCKBOX_DIR "/database_idx.tcd" 83#define TAGCACHE_FILE_MASTER ROCKBOX_DIR "/database_idx.tcd"
84
85/* The main database string data. */
77#define TAGCACHE_FILE_INDEX ROCKBOX_DIR "/database_%d.tcd" 86#define TAGCACHE_FILE_INDEX ROCKBOX_DIR "/database_%d.tcd"
87
88/* ASCII dumpfile of the DB contents. */
78#define TAGCACHE_FILE_CHANGELOG ROCKBOX_DIR "/database_changelog.txt" 89#define TAGCACHE_FILE_CHANGELOG ROCKBOX_DIR "/database_changelog.txt"
90
91/* Serialized DB. */
79#define TAGCACHE_STATEFILE ROCKBOX_DIR "/database_state.tcd" 92#define TAGCACHE_STATEFILE ROCKBOX_DIR "/database_state.tcd"
80 93
81/* Flags */ 94/* Flags */