summaryrefslogtreecommitdiff
path: root/apps/tagcache.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/tagcache.c')
-rw-r--r--apps/tagcache.c1868
1 files changed, 1868 insertions, 0 deletions
diff --git a/apps/tagcache.c b/apps/tagcache.c
new file mode 100644
index 0000000000..6c6dd00a60
--- /dev/null
+++ b/apps/tagcache.c
@@ -0,0 +1,1868 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Miika Pekkarinen
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include <stdio.h>
21#include "thread.h"
22#include "kernel.h"
23#include "system.h"
24#include "logf.h"
25#include "string.h"
26#include "usb.h"
27#include "dircache.h"
28#include "metadata.h"
29#include "id3.h"
30#include "settings.h"
31#include "splash.h"
32#include "lang.h"
33#include "tagcache.h"
34
35/* External reference to the big audiobuffer. */
36extern char *audiobuf, *audiobufend;
37
38/* Tag Cache thread. */
39static struct event_queue tagcache_queue;
40static long tagcache_stack[(DEFAULT_STACK_SIZE + 0x4000)/sizeof(long)];
41static const char tagcache_thread_name[] = "tagcache";
42
43/* Previous path when scanning directory tree recursively. */
44static char curpath[MAX_PATH*2];
45static long curpath_size = sizeof(curpath);
46
47/* Used when removing duplicates. */
48static char *tempbuf; /* Allocated when needed. */
49static long tempbufidx; /* Current location in buffer. */
50static long tempbuf_size; /* Buffer size (TEMPBUF_SIZE). */
51static long tempbuf_left; /* Buffer space left. */
52static long tempbuf_pos;
53
54/* Tags we want to be unique (loaded to the tempbuf). */
55static const int unique_tags[] = { tag_artist, tag_album, tag_genre, tag_title };
56
57/* Queue commands. */
58#define Q_STOP_SCAN 0
59#define Q_START_SCAN 1
60#define Q_FORCE_UPDATE 2
61
62/* Tag database files. */
63#define TAGCACHE_FILE_TEMP ROCKBOX_DIR "/tagcache_tmp.tcd"
64#define TAGCACHE_FILE_MASTER ROCKBOX_DIR "/tagcache_idx.tcd"
65#define TAGCACHE_FILE_INDEX ROCKBOX_DIR "/tagcache_%d.tcd"
66
67/* Tag database structures. */
68#define TAGCACHE_MAGIC 0x01020316
69
70/* Variable-length tag entry in tag files. */
71struct tagfile_entry {
72 short tag_length;
73 char tag_data[0];
74};
75
76/* Fixed-size tag entry in master db index. */
77struct index_entry {
78 long tag_seek[TAG_COUNT];
79};
80
81/* Header is the same in every file. */
82struct tagcache_header {
83 long magic;
84 long datasize;
85 long entry_count;
86};
87
88#ifdef HAVE_TC_RAMCACHE
89/* Header is created when loading database to ram. */
90struct ramcache_header {
91 struct tagcache_header h;
92 struct index_entry *indices;
93 char *tags[TAG_COUNT];
94 int entry_count[TAG_COUNT];
95};
96
97static struct ramcache_header *hdr;
98static bool ramcache = false;
99static long tagcache_size = 0;
100#endif
101
102/**
103 * Full tag entries stored in a temporary file waiting
104 * for commit to the cache. */
105struct temp_file_entry {
106 long tag_offset[TAG_COUNT];
107 short tag_length[TAG_COUNT];
108
109 long data_length;
110};
111
112struct tempbuf_id {
113 int id;
114 struct tempbuf_id *next;
115};
116
117struct tempbuf_searchidx {
118 struct tempbuf_id *id;
119 char *str;
120 int seek;
121};
122
123
124/* Used when building the temporary file. */
125static int cachefd = -1, filenametag_fd;
126static int total_entry_count = 0;
127static int data_size = 0;
128static int processed_dir_count;
129
130#ifdef HAVE_TC_RAMCACHE
131static struct index_entry *find_entry_ram(const char *filename,
132 const struct dircache_entry *dc)
133{
134 static long last_pos = 0;
135 int counter = 0;
136 int i;
137
138 /* Check if we tagcache is loaded into ram. */
139 if (!ramcache)
140 return NULL;
141
142 if (dc == NULL)
143 dc = dircache_get_entry_ptr(filename);
144
145 if (dc == NULL)
146 {
147 logf("tagcache: file not found.");
148 return NULL;
149 }
150
151 try_again:
152
153 if (last_pos > 0)
154 i = last_pos;
155 else
156 i = 0;
157
158 for (; i < hdr->h.entry_count - last_pos; i++)
159 {
160 if (hdr->indices[i].tag_seek[tag_filename] == (long)dc)
161 {
162 last_pos = MAX(0, i - 3);
163 return &hdr->indices[i];
164 }
165
166 if (++counter == 100)
167 {
168 yield();
169 counter = 0;
170 }
171 }
172
173 if (last_pos > 0)
174 {
175 last_pos = 0;
176 goto try_again;
177 }
178
179 return NULL;
180}
181#endif
182
183static struct index_entry *find_entry_disk(const char *filename, bool retrieve)
184{
185 static struct index_entry idx;
186 static long last_pos = -1;
187 long pos_history[POS_HISTORY_COUNT];
188 long pos_history_idx = 0;
189 struct tagcache_header tch;
190 bool found = false;
191 struct tagfile_entry tfe;
192 int masterfd, fd = filenametag_fd;
193 char buf[MAX_PATH];
194 int i;
195 int pos = -1;
196
197 if (fd < 0)
198 {
199 last_pos = -1;
200 return NULL;
201 }
202
203 check_again:
204
205 if (last_pos > 0)
206 lseek(fd, last_pos, SEEK_SET);
207 else
208 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
209
210 while (true)
211 {
212 pos = lseek(fd, 0, SEEK_CUR);
213 for (i = pos_history_idx-1; i >= 0; i--)
214 pos_history[i+1] = pos_history[i];
215 pos_history[0] = pos;
216
217 if (read(fd, &tfe, sizeof(struct tagfile_entry)) !=
218 sizeof(struct tagfile_entry))
219 {
220 break ;
221 }
222
223 if (tfe.tag_length >= (long)sizeof(buf))
224 {
225 logf("too long tag");
226 close(fd);
227 last_pos = -1;
228 return NULL;
229 }
230
231 if (read(fd, buf, tfe.tag_length) != tfe.tag_length)
232 {
233 logf("read error #2");
234 close(fd);
235 last_pos = -1;
236 return NULL;
237 }
238
239 if (!strcasecmp(filename, buf))
240 {
241 last_pos = pos_history[pos_history_idx];
242 found = true;
243 break ;
244 }
245
246 if (pos_history_idx < POS_HISTORY_COUNT - 1)
247 pos_history_idx++;
248 }
249
250 /* Not found? */
251 if (!found)
252 {
253 if (last_pos > 0)
254 {
255 last_pos = -1;
256 logf("seek again");
257 goto check_again;
258 }
259 //close(fd);
260 return NULL;
261 }
262
263 if (!retrieve)
264 {
265 /* Just return a valid pointer without a valid entry. */
266 return &idx;
267 }
268
269 /* Found. Now read the index_entry (if requested). */
270 masterfd = open(TAGCACHE_FILE_MASTER, O_RDONLY);
271 if (masterfd < 0)
272 {
273 logf("open fail");
274 return NULL;
275 }
276
277 if (read(fd, &tch, sizeof(struct tagcache_header)) !=
278 sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC)
279 {
280 logf("header error");
281 return NULL;
282 }
283
284 for (i = 0; i < tch.entry_count; i++)
285 {
286 if (read(masterfd, &idx, sizeof(struct index_entry)) !=
287 sizeof(struct index_entry))
288 {
289 logf("read error #3");
290 close(fd);
291 return NULL;
292 }
293
294 if (idx.tag_seek[tag_filename] == pos)
295 break ;
296 }
297 close(masterfd);
298
299 /* Not found? */
300 if (i == tch.entry_count)
301 {
302 logf("not found!");
303 return NULL;
304 }
305
306 return &idx;
307}
308
309static bool build_lookup_list(struct tagcache_search *tcs)
310{
311 struct tagcache_header header;
312 struct index_entry entry;
313 int masterfd;
314 int i;
315
316 tcs->seek_list_count = 0;
317
318#ifdef HAVE_TC_RAMCACHE
319 if (tcs->ramsearch)
320 {
321 int j;
322
323 for (i = tcs->seek_pos; i < hdr->h.entry_count - tcs->seek_pos; i++)
324 {
325 if (tcs->seek_list_count == SEEK_LIST_SIZE)
326 break ;
327
328 for (j = 0; j < tcs->filter_count; j++)
329 {
330 if (hdr->indices[i].tag_seek[tcs->filter_tag[j]] !=
331 tcs->filter_seek[j])
332 break ;
333 }
334
335 if (j < tcs->filter_count)
336 continue ;
337
338 /* Add to the seek list if not already there. */
339 for (j = 0; j < tcs->seek_list_count; j++)
340 {
341 if (tcs->seek_list[j] == hdr->indices[i].tag_seek[tcs->type])
342 break ;
343 }
344
345 /* Lets add it. */
346 if (j == tcs->seek_list_count)
347 {
348 tcs->seek_list[tcs->seek_list_count] =
349 hdr->indices[i].tag_seek[tcs->type];
350 tcs->seek_list_count++;
351 }
352 }
353
354 tcs->seek_pos = i;
355
356 return tcs->seek_list_count > 0;
357 }
358#endif
359
360 masterfd = open(TAGCACHE_FILE_MASTER, O_RDONLY);
361 if (masterfd < 0)
362 {
363 logf("cannot open master index");
364 return false;
365 }
366
367 /* Load the header. */
368 if (read(masterfd, &header, sizeof(struct tagcache_header)) !=
369 sizeof(struct tagcache_header) || header.magic != TAGCACHE_MAGIC)
370 {
371 logf("read error");
372 close(masterfd);
373 return false;
374 }
375
376 lseek(masterfd, tcs->seek_pos * sizeof(struct index_entry) +
377 sizeof(struct tagcache_header), SEEK_SET);
378
379 while (read(masterfd, &entry, sizeof(struct index_entry)) ==
380 sizeof(struct index_entry))
381 {
382 if (tcs->seek_list_count == SEEK_LIST_SIZE)
383 break ;
384
385 for (i = 0; i < tcs->filter_count; i++)
386 {
387 if (entry.tag_seek[tcs->filter_tag[i]] != tcs->filter_seek[i])
388 break ;
389 }
390
391 tcs->seek_pos++;
392
393 if (i < tcs->filter_count)
394 continue ;
395
396 /* Add to the seek list if not already there. */
397 for (i = 0; i < tcs->seek_list_count; i++)
398 {
399 if (tcs->seek_list[i] == entry.tag_seek[tcs->type])
400 break ;
401 }
402
403 /* Lets add it. */
404 if (i == tcs->seek_list_count)
405 {
406 tcs->seek_list[tcs->seek_list_count] =
407 entry.tag_seek[tcs->type];
408 tcs->seek_list_count++;
409 }
410
411 }
412 close(masterfd);
413
414 return tcs->seek_list_count > 0;
415}
416
417bool tagcache_search(struct tagcache_search *tcs, int tag)
418{
419 struct tagcache_header h;
420 char buf[MAX_PATH];
421
422 if (tcs->valid)
423 tagcache_search_finish(tcs);
424
425 tcs->position = sizeof(struct tagcache_header);
426 tcs->fd = -1;
427 tcs->type = tag;
428 tcs->seek_pos = 0;
429 tcs->seek_list_count = 0;
430 tcs->filter_count = 0;
431 tcs->valid = true;
432
433#ifndef HAVE_TC_RAMCACHE
434 tcs->ramsearch = false;
435#else
436 tcs->ramsearch = ramcache;
437 if (tcs->ramsearch)
438 {
439 tcs->entry_count = hdr->entry_count[tcs->type];
440 }
441 else
442#endif
443 {
444 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, tcs->type);
445 tcs->fd = open(buf, O_RDONLY);
446 if (tcs->fd < 0)
447 {
448 logf("failed to open index");
449 return false;
450 }
451
452 /* Check the header. */
453 if (read(tcs->fd, &h, sizeof(struct tagcache_header)) !=
454 sizeof(struct tagcache_header) || h.magic != TAGCACHE_MAGIC)
455 {
456 logf("incorrect header");
457 return false;
458 }
459 }
460
461 return true;
462}
463
464bool tagcache_search_add_filter(struct tagcache_search *tcs,
465 int tag, int seek)
466{
467 if (tcs->filter_count == TAGCACHE_MAX_FILTERS)
468 return false;
469
470 tcs->filter_tag[tcs->filter_count] = tag;
471 tcs->filter_seek[tcs->filter_count] = seek;
472 tcs->filter_count++;
473
474 return true;
475}
476
477bool tagcache_get_next(struct tagcache_search *tcs)
478{
479 static char buf[MAX_PATH];
480 struct tagfile_entry entry;
481
482 if (!tcs->valid)
483 return false;
484
485 if (tcs->fd < 0
486#ifdef HAVE_TC_RAMCACHE
487 && !tcs->ramsearch
488#endif
489 )
490 return false;
491
492 /* Relative fetch. */
493 if (tcs->filter_count > 0)
494 {
495 /* Check for end of list. */
496 if (tcs->seek_list_count == 0)
497 {
498 /* Try to fetch more. */
499 if (!build_lookup_list(tcs))
500 return false;
501 }
502
503 tcs->seek_list_count--;
504
505 /* Seek stream to the correct position and continue to direct fetch. */
506 if (!tcs->ramsearch)
507 lseek(tcs->fd, tcs->seek_list[tcs->seek_list_count], SEEK_SET);
508 else
509 tcs->position = tcs->seek_list[tcs->seek_list_count];
510 }
511
512 /* Direct fetch. */
513#ifdef HAVE_TC_RAMCACHE
514 if (tcs->ramsearch)
515 {
516 struct tagfile_entry *ep;
517
518 if (tcs->entry_count == 0)
519 {
520 tcs->valid = false;
521 return false;
522 }
523 tcs->entry_count--;
524 tcs->result_seek = tcs->position;
525
526 if (tcs->type == tag_filename)
527 {
528 dircache_copy_path((struct dircache_entry *)tcs->position,
529 buf, sizeof buf);
530 tcs->result = buf;
531 tcs->result_len = strlen(buf) + 1;
532
533 return true;
534 }
535
536 ep = (struct tagfile_entry *)&hdr->tags[tcs->type][tcs->position];
537 tcs->position += sizeof(struct tagfile_entry) + ep->tag_length;
538 tcs->result = ep->tag_data;
539 tcs->result_len = ep->tag_length;
540
541 return true;
542 }
543 else
544#endif
545 {
546 tcs->result_seek = lseek(tcs->fd, 0, SEEK_CUR);
547 if (read(tcs->fd, &entry, sizeof(struct tagfile_entry)) !=
548 sizeof(struct tagfile_entry))
549 {
550 /* End of data. */
551 tcs->valid = false;
552 return false;
553 }
554 }
555
556 if (entry.tag_length > (long)sizeof(buf))
557 {
558 tcs->valid = false;
559 logf("too long tag");
560 return false;
561 }
562
563 if (read(tcs->fd, buf, entry.tag_length) != entry.tag_length)
564 {
565 tcs->valid = false;
566 logf("read error");
567 return false;
568 }
569
570 tcs->result = buf;
571 tcs->result_len = entry.tag_length;
572
573 return true;
574}
575
576#if 0
577void tagcache_modify(struct tagcache_search *tcs, int type, const char *text)
578{
579 struct tagentry *entry;
580
581 if (tcs->type != tag_title)
582 return ;
583
584 /* We will need reserve buffer for this. */
585 if (tcs->ramcache)
586 {
587 struct tagfile_entry *ep;
588
589 ep = (struct tagfile_entry *)&hdr->tags[tcs->type][tcs->result_seek];
590 tcs->seek_list[tcs->seek_list_count];
591 }
592
593 entry = find_entry_ram(
594
595}
596#endif
597
598void tagcache_search_finish(struct tagcache_search *tcs)
599{
600 if (tcs->fd >= 0)
601 {
602 close(tcs->fd);
603 tcs->fd = -1;
604 tcs->ramsearch = false;
605 tcs->valid = false;
606 }
607}
608
609#ifdef HAVE_TC_RAMCACHE
610struct tagfile_entry *get_tag(struct index_entry *entry, int tag)
611{
612 return (struct tagfile_entry *)&hdr->tags[tag][entry->tag_seek[tag]];
613}
614
615bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
616{
617 struct index_entry *entry;
618
619 /* Find the corresponding entry in tagcache. */
620 entry = find_entry_ram(filename, NULL);
621 if (entry == NULL || !ramcache)
622 return false;
623
624 id3->title = get_tag(entry, tag_title)->tag_data;
625 id3->artist = get_tag(entry, tag_artist)->tag_data;
626 id3->album = get_tag(entry, tag_album)->tag_data;
627 id3->genre_string = get_tag(entry, tag_genre)->tag_data;
628
629 return true;
630}
631#endif
632
633static inline void write_item(const char *item)
634{
635 int len = strlen(item) + 1;
636
637 data_size += len;
638 write(cachefd, item, len);
639}
640
641inline void check_if_empty(char **tag)
642{
643 if (tag == NULL || *tag == NULL || *tag[0] == '\0')
644 *tag = "Unknown";
645}
646
647#define CRC_BUF_LEN 8
648
649#ifdef HAVE_TC_RAMCACHE
650static void add_tagcache(const char *path, const struct dircache_entry *dc)
651#else
652static void add_tagcache(const char *path)
653#endif
654{
655 struct track_info track;
656 struct temp_file_entry entry;
657 bool ret;
658 int fd;
659 //uint32_t crcbuf[CRC_BUF_LEN];
660
661 if (cachefd < 0)
662 return ;
663
664 /* Check if the file is supported. */
665 if (probe_file_format(path) == AFMT_UNKNOWN)
666 return ;
667
668 /* Check if the file is already cached. */
669#ifdef HAVE_TC_RAMCACHE
670 if (ramcache)
671 {
672 if (find_entry_ram(path, dc))
673 return ;
674 }
675 else
676#endif
677 {
678 if (find_entry_disk(path, false))
679 return ;
680 }
681
682 fd = open(path, O_RDONLY);
683 if (fd < 0)
684 {
685 logf("open fail: %s", path);
686 return ;
687 }
688
689 memset(&track, 0, sizeof(struct track_info));
690 ret = get_metadata(&track, fd, path, false);
691 close(fd);
692
693 if (!ret)
694 {
695 track.id3.title = (char *)path;
696 track.id3.artist = "Unknown";
697 track.id3.album = "Unknown";
698 track.id3.genre_string = "Unknown";
699 }
700 else
701 check_if_empty(&track.id3.title);
702 check_if_empty(&track.id3.artist);
703 check_if_empty(&track.id3.album);
704 check_if_empty(&track.id3.genre_string);
705
706 entry.tag_length[tag_filename] = strlen(path) + 1;
707 entry.tag_length[tag_title] = strlen(track.id3.title) + 1;
708 entry.tag_length[tag_artist] = strlen(track.id3.artist) + 1;
709 entry.tag_length[tag_album] = strlen(track.id3.album) + 1;
710 entry.tag_length[tag_genre] = strlen(track.id3.genre_string) + 1;
711
712 entry.tag_offset[tag_filename] = 0;
713 entry.tag_offset[tag_title] = entry.tag_offset[tag_filename] + entry.tag_length[tag_filename];
714 entry.tag_offset[tag_artist] = entry.tag_offset[tag_title] + entry.tag_length[tag_title];
715 entry.tag_offset[tag_album] = entry.tag_offset[tag_artist] + entry.tag_length[tag_artist];
716 entry.tag_offset[tag_genre] = entry.tag_offset[tag_album] + entry.tag_length[tag_album];
717 entry.data_length = entry.tag_offset[tag_genre] + entry.tag_length[tag_genre];
718
719 write(cachefd, &entry, sizeof(struct temp_file_entry));
720 write_item(path);
721 write_item(track.id3.title);
722 write_item(track.id3.artist);
723 write_item(track.id3.album);
724 write_item(track.id3.genre_string);
725 total_entry_count++;
726}
727
728static void remove_files(void)
729{
730 int i;
731 char buf[MAX_PATH];
732
733 remove(TAGCACHE_FILE_MASTER);
734 for (i = 0; i < TAG_COUNT; i++)
735 {
736 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, i);
737 remove(buf);
738 }
739}
740
741static bool tempbuf_insert(char *str, int id)
742{
743 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
744 int len = strlen(str)+1;
745
746 /* Insert it to the buffer. */
747 tempbuf_left -= len + sizeof(struct tempbuf_id);
748 if (tempbuf_left - 4 < 0 || tempbufidx >= TAGFILE_MAX_ENTRIES-1)
749 return false;
750
751 index[tempbufidx].id = (struct tempbuf_id *)&tempbuf[tempbuf_pos];
752#ifdef ROCKBOX_STRICT_ALIGN
753 /* Make sure the entry is long aligned. */
754 if ((long)index[tempbufidx].id & 0x03)
755 {
756 int fix = 4 - ((long)id & 0x03);
757 tempbuf_left -= fix;
758 tempbuf_pos += fix;
759 index[tempbufidx].id = (struct tempbuf_id *)((
760 (long)index[tempbufidx].id & ~0x03) + 0x04);
761 }
762#endif
763 index[tempbufidx].id->id = id;
764 index[tempbufidx].id->next = NULL;
765 tempbuf_pos += sizeof(struct tempbuf_id);
766
767 index[tempbufidx].seek = -1;
768 index[tempbufidx].str = &tempbuf[tempbuf_pos];
769 memcpy(index[tempbufidx].str, str, len);
770 tempbuf_pos += len;
771 tempbufidx++;
772
773 return true;
774}
775
776static bool tempbuf_unique_insert(char *str, int id)
777{
778 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
779 struct tempbuf_id *idp;
780 int i;
781
782 /* Check if string already exists. */
783 for (i = 0; i < tempbufidx; i++)
784 {
785 if (!strcasecmp(str, index[i].str))
786 {
787 tempbuf_left -= sizeof(struct tempbuf_id);
788 if (tempbuf_left < 0)
789 return false;
790
791 idp = index[i].id;
792 while (idp->next != NULL)
793 idp = idp->next;
794
795 idp->next = (struct tempbuf_id *)&tempbuf[tempbuf_pos];
796 idp = idp->next;
797 idp->id = id;
798 idp->next = NULL;
799 tempbuf_pos += sizeof(struct tempbuf_id);
800
801 return true;
802 }
803 }
804
805 return tempbuf_insert(str, id);
806}
807
808static int compare(const void *p1, const void *p2)
809{
810 struct tempbuf_searchidx *e1 = (struct tempbuf_searchidx *)p1;
811 struct tempbuf_searchidx *e2 = (struct tempbuf_searchidx *)p2;
812
813 /*
814 if (!strncasecmp("the ", e1, 4))
815 e1 = &e1[4];
816 if (!strncasecmp("the ", e2, 4))
817 e2 = &e2[4];
818 */
819
820 return strncasecmp(e1->str, e2->str, MAX_PATH);
821}
822
823static int tempbuf_sort(int fd)
824{
825 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
826 struct tagfile_entry fe;
827 int i;
828
829 qsort(index, tempbufidx, sizeof(struct tempbuf_searchidx), compare);
830
831 for (i = 0; i < tempbufidx; i++)
832 {
833 index[i].seek = lseek(fd, 0, SEEK_CUR);
834 fe.tag_length = strlen(index[i].str) + 1;
835 if (write(fd, &fe, sizeof(struct tagfile_entry)) !=
836 sizeof(struct tagfile_entry))
837 {
838 logf("tempbuf_sort: write error #1");
839 return -1;
840 }
841
842 if (write(fd, index[i].str, fe.tag_length) !=
843 fe.tag_length)
844 {
845 logf("tempbuf_sort: write error #2");
846 return -2;
847 }
848 }
849
850 return i;
851}
852
853
854static struct tempbuf_searchidx* tempbuf_locate(int id)
855{
856 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
857 struct tempbuf_id *idp;
858 int i;
859
860 /* Check if string already exists. */
861 for (i = 0; i < tempbufidx; i++)
862 {
863 idp = index[i].id;
864 while (idp != NULL)
865 {
866 if (idp->id == id)
867 return &index[i];
868 idp = idp->next;
869 }
870 }
871
872 return NULL;
873}
874
875
876static int tempbuf_find_location(int id)
877{
878 struct tempbuf_searchidx *entry;
879
880 entry = tempbuf_locate(id);
881 if (entry == NULL)
882 return -1;
883
884 return entry->seek;
885}
886
887static bool is_unique_tag(int type)
888{
889 int i;
890
891 for (i = 0; i < (int)(sizeof(unique_tags)/sizeof(unique_tags[0])); i++)
892 {
893 if (type == unique_tags[i])
894 return true;
895 }
896
897 return false;
898}
899
900static bool build_index(int index_type, struct tagcache_header *h, int tmpfd)
901{
902 int i;
903 struct tagcache_header tch;
904 struct index_entry idx;
905 char buf[MAX_PATH];
906 int fd = -1, masterfd;
907 bool error = false;
908 int init;
909 int masterfd_pos;
910
911 logf("Building index: %d", index_type);
912
913 tempbufidx = 0;
914 tempbuf_pos = TAGFILE_MAX_ENTRIES * sizeof(struct tempbuf_searchidx);
915 tempbuf_left = tempbuf_size - tempbuf_pos;
916 if (tempbuf_left < 0)
917 {
918 logf("Buffer way too small!");
919 return false;
920 }
921
922 /* Open the index file, which contains the tag names. */
923 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, index_type);
924 fd = open(buf, O_RDWR);
925
926 if (fd >= 0)
927 {
928 /* Read the header. */
929 if (read(fd, &tch, sizeof(struct tagcache_header)) !=
930 sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC)
931 {
932 logf("header error");
933 close(fd);
934 return false;
935 }
936
937 /**
938 * If tag file contains unique tags (sorted index), we will load
939 * it entirely into memory so we can resort it later for use with
940 * chunked browsing.
941 */
942 if (is_unique_tag(index_type))
943 {
944 for (i = 0; i < tch.entry_count; i++)
945 {
946 struct tagfile_entry entry;
947 int loc = lseek(fd, 0, SEEK_CUR);
948
949 if (read(fd, &entry, sizeof(struct tagfile_entry))
950 != sizeof(struct tagfile_entry))
951 {
952 logf("read error");
953 close(fd);
954 return false;
955 }
956
957 if (entry.tag_length >= (int)sizeof(buf))
958 {
959 logf("too long tag");
960 close(fd);
961 return false;
962 }
963
964 if (read(fd, buf, entry.tag_length) != entry.tag_length)
965 {
966 logf("read error #2");
967 close(fd);
968 return false;
969 }
970
971 /**
972 * Save the tag and tag id in the memory buffer. Tag id
973 * is saved so we can later reindex the master lookup
974 * table when the index gets resorted.
975 */
976 tempbuf_insert(buf, loc + TAGFILE_MAX_ENTRIES);
977 }
978 }
979 else
980 tempbufidx = tch.entry_count;
981 }
982 else
983 {
984 /**
985 * Creating new index file to store the tags. No need to preload
986 * anything whether the index type is sorted or not.
987 */
988 fd = open(buf, O_WRONLY | O_CREAT | O_TRUNC);
989 if (fd < 0)
990 {
991 logf("%s open fail", buf);
992 return false;
993 }
994
995 tch.magic = TAGCACHE_MAGIC;
996 tch.entry_count = 0;
997 tch.datasize = 0;
998
999 if (write(fd, &tch, sizeof(struct tagcache_header)) !=
1000 sizeof(struct tagcache_header))
1001 {
1002 logf("header write failed");
1003 close(fd);
1004 return false;
1005 }
1006 }
1007
1008 /* Loading the tag lookup file as "master file". */
1009 logf("Loading index file");
1010 masterfd = open(TAGCACHE_FILE_MASTER, O_RDWR);
1011
1012 if (masterfd < 0)
1013 {
1014 logf("Creating new index");
1015 masterfd = open(TAGCACHE_FILE_MASTER, O_WRONLY | O_CREAT | O_TRUNC);
1016
1017 if (masterfd < 0)
1018 {
1019 logf("Failure to create index file");
1020 close(fd);
1021 return false;
1022 }
1023
1024 /* Write the header (write real values later). */
1025 tch = *h;
1026 tch.entry_count = 0;
1027 tch.datasize = 0;
1028 write(masterfd, &tch, sizeof(struct tagcache_header));
1029 init = true;
1030 masterfd_pos = lseek(masterfd, 0, SEEK_CUR);
1031 }
1032 else
1033 {
1034 /**
1035 * Master file already exists so we need to process the current
1036 * file first.
1037 */
1038 init = false;
1039
1040 if (read(masterfd, &tch, sizeof(struct tagcache_header)) !=
1041 sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC)
1042 {
1043 logf("header error");
1044 close(fd);
1045 close(masterfd);
1046 return false;
1047 }
1048
1049 /**
1050 * If we reach end of the master file, we need to expand it to
1051 * hold new tags. If the current index is not sorted, we can
1052 * simply append new data to end of the file.
1053 * However, if the index is sorted, we need to update all tag
1054 * pointers in the master file for the current index.
1055 */
1056 masterfd_pos = lseek(masterfd, tch.entry_count * sizeof(struct index_entry),
1057 SEEK_CUR);
1058 if (masterfd_pos == filesize(masterfd))
1059 {
1060 logf("appending...");
1061 init = true;
1062 }
1063 }
1064
1065 /**
1066 * Load new unique tags in memory to be sorted later and added
1067 * to the master lookup file.
1068 */
1069 if (is_unique_tag(index_type))
1070 {
1071 lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
1072 /* h is the header of the temporary file containing new tags. */
1073 for (i = 0; i < h->entry_count; i++)
1074 {
1075 struct temp_file_entry entry;
1076
1077 if (read(tmpfd, &entry, sizeof(struct temp_file_entry)) !=
1078 sizeof(struct temp_file_entry))
1079 {
1080 logf("read fail #1");
1081 error = true;
1082 goto error_exit;
1083 }
1084
1085 /* Read data. */
1086 if (entry.tag_length[index_type] >= (long)sizeof(buf))
1087 {
1088 logf("too long entry!");
1089 error = true;
1090 goto error_exit;
1091 }
1092
1093 lseek(tmpfd, entry.tag_offset[index_type], SEEK_CUR);
1094 if (read(tmpfd, buf, entry.tag_length[index_type]) !=
1095 entry.tag_length[index_type])
1096 {
1097 logf("read fail #3");
1098 error = true;
1099 goto error_exit;
1100 }
1101
1102 if (!tempbuf_unique_insert(buf, i))
1103 {
1104 logf("insert error");
1105 error = true;
1106 goto error_exit;
1107 }
1108
1109 /* Skip to next. */
1110 lseek(tmpfd, entry.data_length - entry.tag_offset[index_type] -
1111 entry.tag_length[index_type], SEEK_CUR);
1112 }
1113
1114 /* Sort the buffer data and write it to the index file. */
1115 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
1116 i = tempbuf_sort(fd);
1117 if (i < 0)
1118 goto error_exit;
1119 logf("sorted %d tags", i);
1120
1121 /**
1122 * Now update all indexes in the master lookup file.
1123 */
1124 lseek(masterfd, sizeof(struct tagcache_header), SEEK_SET);
1125 for (i = 0; i < tch.entry_count; i++)
1126 {
1127 int loc = lseek(masterfd, 0, SEEK_CUR);
1128
1129 if (read(masterfd, &idx, sizeof(struct index_entry)) !=
1130 sizeof(struct index_entry))
1131 {
1132 logf("read fail #2");
1133 error = true;
1134 goto error_exit ;
1135 }
1136 idx.tag_seek[index_type] = tempbuf_find_location(
1137 idx.tag_seek[index_type]+TAGFILE_MAX_ENTRIES);
1138 if (idx.tag_seek[index_type] < 0)
1139 {
1140 logf("update error: %d/%d", i, tch.entry_count);
1141 error = true;
1142 goto error_exit;
1143 }
1144
1145 /* Write back the updated index. */
1146 lseek(masterfd, loc, SEEK_SET);
1147 if (write(masterfd, &idx, sizeof(struct index_entry)) !=
1148 sizeof(struct index_entry))
1149 {
1150 logf("write fail");
1151 error = true;
1152 goto error_exit;
1153 }
1154 }
1155 }
1156
1157 /**
1158 * Walk through the temporary file containing the new tags.
1159 */
1160 // build_normal_index(h, tmpfd, masterfd, idx);
1161 lseek(masterfd, masterfd_pos, SEEK_SET);
1162 lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
1163 lseek(fd, 0, SEEK_END);
1164 for (i = 0; i < h->entry_count; i++)
1165 {
1166 if (init)
1167 {
1168 memset(&idx, 0, sizeof(struct index_entry));
1169 }
1170 else
1171 {
1172 if (read(masterfd, &idx, sizeof(struct index_entry)) !=
1173 sizeof(struct index_entry))
1174 {
1175 logf("read fail #2");
1176 error = true;
1177 break ;
1178 }
1179 lseek(masterfd, -sizeof(struct index_entry), SEEK_CUR);
1180 }
1181
1182 /* Read entry headers. */
1183 if (!is_unique_tag(index_type))
1184 {
1185 struct temp_file_entry entry;
1186 struct tagfile_entry fe;
1187
1188 if (read(tmpfd, &entry, sizeof(struct temp_file_entry)) !=
1189 sizeof(struct temp_file_entry))
1190 {
1191 logf("read fail #1");
1192 error = true;
1193 break ;
1194 }
1195
1196 /* Read data. */
1197 if (entry.tag_length[index_type] >= (int)sizeof(buf))
1198 {
1199 logf("too long entry!");
1200 logf("length=%d", entry.tag_length[index_type]);
1201 logf("pos=0x%02x", lseek(tmpfd, 0, SEEK_CUR));
1202 error = true;
1203 break ;
1204 }
1205
1206 lseek(tmpfd, entry.tag_offset[index_type], SEEK_CUR);
1207 if (read(tmpfd, buf, entry.tag_length[index_type]) !=
1208 entry.tag_length[index_type])
1209 {
1210 logf("read fail #3");
1211 logf("offset=0x%02x", entry.tag_offset[index_type]);
1212 logf("length=0x%02x", entry.tag_length[index_type]);
1213 error = true;
1214 break ;
1215 }
1216
1217 /* Write to index file. */
1218 idx.tag_seek[index_type] = lseek(fd, 0, SEEK_CUR);
1219 fe.tag_length = entry.tag_length[index_type];
1220 write(fd, &fe, sizeof(struct tagfile_entry));
1221 write(fd, buf, fe.tag_length);
1222 tempbufidx++;
1223
1224 /* Skip to next. */
1225 lseek(tmpfd, entry.data_length - entry.tag_offset[index_type] -
1226 entry.tag_length[index_type], SEEK_CUR);
1227 }
1228 else
1229 {
1230 /* Locate the correct entry from the sorted array. */
1231 idx.tag_seek[index_type] = tempbuf_find_location(i);
1232 if (idx.tag_seek[index_type] < 0)
1233 {
1234 logf("entry not found (%d)");
1235 error = true;
1236 break ;
1237 }
1238 }
1239
1240
1241 /* Write index. */
1242 if (write(masterfd, &idx, sizeof(struct index_entry)) !=
1243 sizeof(struct index_entry))
1244 {
1245 logf("tagcache: write fail #4");
1246 error = true;
1247 break ;
1248 }
1249
1250 }
1251
1252 /* Finally write the uniqued tag index file. */
1253 if (is_unique_tag(index_type))
1254 {
1255 tch.magic = TAGCACHE_MAGIC;
1256 tch.entry_count = tempbufidx;
1257 tch.datasize = lseek(fd, 0, SEEK_END) - sizeof(struct tagcache_header);
1258 lseek(fd, 0, SEEK_SET);
1259 write(fd, &tch, sizeof(struct tagcache_header));
1260 }
1261 else
1262 {
1263 tch.magic = TAGCACHE_MAGIC;
1264 tch.entry_count = tempbufidx;
1265 tch.datasize = lseek(fd, 0, SEEK_CUR) - sizeof(struct tagcache_header);
1266 lseek(fd, 0, SEEK_SET);
1267 write(fd, &tch, sizeof(struct tagcache_header));
1268 }
1269
1270 error_exit:
1271
1272 close(fd);
1273 close(masterfd);
1274
1275 return !error;
1276}
1277
1278static bool commit(void)
1279{
1280 struct tagcache_header header, header_old;
1281 int i, len, rc;
1282 int tmpfd;
1283 int masterfd;
1284
1285 logf("committing tagcache");
1286
1287 tmpfd = open(TAGCACHE_FILE_TEMP, O_RDONLY);
1288 if (tmpfd < 0)
1289 {
1290 logf("nothing to commit");
1291 return true;
1292 }
1293
1294 /* Load the header. */
1295 len = sizeof(struct tagcache_header);
1296 rc = read(tmpfd, &header, len);
1297
1298 if (header.magic != TAGCACHE_MAGIC || rc != len)
1299 {
1300 logf("incorrect header");
1301 close(tmpfd);
1302 remove_files();
1303 remove(TAGCACHE_FILE_TEMP);
1304 return false;
1305 }
1306
1307 if (header.entry_count == 0)
1308 {
1309 logf("nothing to commit");
1310 close(tmpfd);
1311 remove(TAGCACHE_FILE_TEMP);
1312 return true;
1313 }
1314
1315 if (tempbuf_size == 0)
1316 {
1317 logf("delaying commit until next boot");
1318 close(tmpfd);
1319 return false;
1320 }
1321
1322 logf("commit %d entries...", header.entry_count);
1323
1324 /* Now create the index files. */
1325 for (i = 0; i < TAG_COUNT; i++)
1326 {
1327 if (!build_index(i, &header, tmpfd))
1328 {
1329 logf("tagcache failed init");
1330 remove_files();
1331 return false;
1332 }
1333 }
1334
1335 close(tmpfd);
1336
1337 /* Update the master index headers. */
1338 masterfd = open(TAGCACHE_FILE_MASTER, O_RDWR);
1339 if (masterfd < 0)
1340 {
1341 logf("failed to open master index");
1342 return false;
1343 }
1344
1345 if (read(masterfd, &header_old, sizeof(struct tagcache_header))
1346 != sizeof(struct tagcache_header) ||
1347 header_old.magic != TAGCACHE_MAGIC)
1348 {
1349 logf("incorrect header");
1350 close(masterfd);
1351 remove_files();
1352 return false;
1353 }
1354
1355 header.entry_count += header_old.entry_count;
1356 header.datasize += header_old.datasize;
1357
1358 lseek(masterfd, 0, SEEK_SET);
1359 write(masterfd, &header, sizeof(struct tagcache_header));
1360 close(masterfd);
1361
1362 logf("tagcache committed");
1363 remove(TAGCACHE_FILE_TEMP);
1364
1365 return true;
1366}
1367
1368static void allocate_tempbuf(void)
1369{
1370 /* Yeah, malloc would be really nice now :) */
1371 tempbuf = (char *)(((long)audiobuf & ~0x03) + 0x04);
1372 tempbuf_size = (int)audiobufend - (int)audiobuf - 4;
1373 audiobuf += tempbuf_size;
1374}
1375
1376static void free_tempbuf(void)
1377{
1378 if (tempbuf_size == 0)
1379 return ;
1380
1381 audiobuf -= tempbuf_size;
1382 tempbuf = NULL;
1383 tempbuf_size = 0;
1384}
1385
1386#ifdef HAVE_TC_RAMCACHE
1387static bool allocate_tagcache(void)
1388{
1389 int rc, len;
1390 int fd;
1391
1392 hdr = NULL;
1393
1394 fd = open(TAGCACHE_FILE_MASTER, O_RDONLY);
1395 if (fd < 0)
1396 {
1397 logf("no tagcache file found.");
1398 return false;
1399 }
1400
1401 /* Load the header. */
1402 hdr = (struct ramcache_header *)audiobuf;
1403 memset(hdr, 0, sizeof(struct ramcache_header));
1404 len = sizeof(struct tagcache_header);
1405 rc = read(fd, &hdr->h, len);
1406 close(fd);
1407
1408 if (hdr->h.magic != TAGCACHE_MAGIC || rc != len)
1409 {
1410 logf("incorrect header");
1411 remove_files();
1412 hdr = NULL;
1413 return false;
1414 }
1415
1416 hdr->indices = (struct index_entry *)(hdr + 1);
1417
1418 /* Now calculate the required cache size. */
1419 tagcache_size = hdr->h.datasize +
1420 sizeof(struct index_entry) * hdr->h.entry_count +
1421 sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *);
1422 logf("tagcache: %d bytes allocated.", tagcache_size);
1423 logf("at: 0x%04x", audiobuf);
1424 audiobuf += (long)((tagcache_size & ~0x03) + 0x04);
1425
1426 return true;
1427}
1428
1429static bool load_tagcache(void)
1430{
1431 struct tagcache_header *tch;
1432 long bytesleft = tagcache_size;
1433 struct index_entry *idx;
1434 int rc, fd;
1435 char *p;
1436 int i;
1437
1438 /* We really need the dircache for this. */
1439 while (!dircache_is_enabled())
1440 sleep(HZ);
1441
1442 logf("loading tagcache to ram...");
1443
1444 fd = open(TAGCACHE_FILE_MASTER, O_RDONLY);
1445 if (fd < 0)
1446 {
1447 logf("tagcache open failed");
1448 return false;
1449 }
1450
1451 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
1452
1453 idx = hdr->indices;
1454
1455 /* Load the master index table. */
1456 for (i = 0; i < hdr->h.entry_count; i++)
1457 {
1458 rc = read(fd, idx, sizeof(struct index_entry));
1459 if (rc != sizeof(struct index_entry))
1460 {
1461 logf("read error #1");
1462 close(fd);
1463 return false;
1464 }
1465
1466 bytesleft -= sizeof(struct index_entry);
1467 if (bytesleft < 0 || ((long)idx - (long)hdr->indices) >= tagcache_size)
1468 {
1469 logf("too big tagcache.");
1470 close(fd);
1471 return false;
1472 }
1473
1474 idx++;
1475 }
1476
1477 close(fd);
1478
1479 /* Load the tags. */
1480 p = (char *)idx;
1481 for (i = 0; i < TAG_COUNT; i++)
1482 {
1483 struct tagfile_entry *fe;
1484 char buf[MAX_PATH];
1485
1486 //p = ((void *)p+1);
1487 p = (char *)((long)p & ~0x03) + 0x04;
1488 hdr->tags[i] = p;
1489
1490 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, i);
1491 fd = open(buf, O_RDONLY);
1492
1493 if (fd < 0)
1494 {
1495 logf("%s open fail", buf);
1496 return false;
1497 }
1498
1499 /* Check the header. */
1500 tch = (struct tagcache_header *)p;
1501 rc = read(fd, tch, sizeof(struct tagcache_header));
1502 p += rc;
1503 if (rc != sizeof(struct tagcache_header) ||
1504 tch->magic != TAGCACHE_MAGIC)
1505 {
1506 logf("incorrect header");
1507 close(fd);
1508 return false;
1509 }
1510
1511 for (hdr->entry_count[i] = 0;
1512 hdr->entry_count[i] < tch->entry_count;
1513 hdr->entry_count[i]++)
1514 {
1515 yield();
1516 fe = (struct tagfile_entry *)p;
1517 rc = read(fd, fe, sizeof(struct tagfile_entry));
1518 if (rc != sizeof(struct tagfile_entry))
1519 {
1520 /* End of lookup table. */
1521 logf("read error");
1522 close(fd);
1523 return false;
1524 }
1525
1526 /* We have a special handling for the filename tags. */
1527 if (i == tag_filename)
1528 {
1529 const struct dircache_entry *dc;
1530
1531 if (fe->tag_length >= (long)sizeof(buf)-1)
1532 {
1533 logf("too long filename");
1534 close(fd);
1535 return false;
1536 }
1537
1538 rc = read(fd, buf, fe->tag_length);
1539 if (rc != fe->tag_length)
1540 {
1541 logf("read error #3");
1542 close(fd);
1543 return false;
1544 }
1545
1546 dc = dircache_get_entry_ptr(buf);
1547 if (dc == NULL)
1548 {
1549 logf("Entry no longer valid.");
1550 logf("-> %s", buf);
1551 continue ;
1552 }
1553
1554 hdr->indices[hdr->entry_count[i]].tag_seek[tag_filename]
1555 = (long)dc;
1556
1557 continue ;
1558 }
1559
1560 bytesleft -= sizeof(struct tagfile_entry) + fe->tag_length;
1561 if (bytesleft < 0)
1562 {
1563 logf("too big tagcache.");
1564 close(fd);
1565 return false;
1566 }
1567
1568 p = fe->tag_data;
1569 rc = read(fd, fe->tag_data, fe->tag_length);
1570 p += rc;
1571
1572 if (rc != fe->tag_length)
1573 {
1574 logf("read error #4");
1575 logf("rc=0x%04x", rc); // 0x431
1576 logf("len=0x%04x", fe->tag_length); // 0x4000
1577 logf("pos=0x%04x", lseek(fd, 0, SEEK_CUR)); // 0x433
1578 logf("i=0x%02x", i); // 0x00
1579 close(fd);
1580 return false;
1581 }
1582 }
1583 close(fd);
1584 }
1585
1586 logf("tagcache loaded into ram!");
1587
1588 return true;
1589}
1590#endif
1591
1592static bool check_dir(const char *dirname)
1593{
1594 DIRCACHED *dir;
1595 int len;
1596 int success = false;
1597
1598 dir = opendir_cached(dirname);
1599 if (!dir)
1600 {
1601 logf("tagcache: opendir_cached() failed");
1602 return false;
1603 }
1604
1605 /* Recursively scan the dir. */
1606 while (queue_empty(&tagcache_queue))
1607 {
1608 struct dircache_entry *entry;
1609
1610 entry = readdir_cached(dir);
1611
1612 if (entry == NULL)
1613 {
1614 success = true;
1615 break ;
1616 }
1617
1618 if (!strcmp(entry->d_name, ".") ||
1619 !strcmp(entry->d_name, ".."))
1620 continue;
1621
1622 yield();
1623
1624 len = strlen(curpath);
1625 snprintf(&curpath[len], curpath_size - len, "/%s",
1626 entry->d_name);
1627
1628 processed_dir_count++;
1629 if (entry->attribute & ATTR_DIRECTORY)
1630 check_dir(curpath);
1631 else
1632#ifdef HAVE_TC_RAMCACHE
1633 add_tagcache(curpath, dir->internal_entry);
1634#else
1635 add_tagcache(curpath);
1636#endif
1637
1638 curpath[len] = '\0';
1639 }
1640
1641 closedir_cached(dir);
1642
1643 return success;
1644}
1645
1646static void build_tagcache(void)
1647{
1648 struct tagcache_header header;
1649 bool ret;
1650 char buf[MAX_PATH];
1651
1652 curpath[0] = '\0';
1653 data_size = 0;
1654 total_entry_count = 0;
1655 processed_dir_count = 0;
1656
1657 logf("updating tagcache");
1658
1659 cachefd = open(TAGCACHE_FILE_TEMP, O_RDONLY);
1660 if (cachefd >= 0)
1661 {
1662 logf("skipping, cache already waiting for commit");
1663 close(cachefd);
1664 return ;
1665 }
1666
1667 cachefd = open(TAGCACHE_FILE_TEMP, O_RDWR | O_CREAT | O_TRUNC);
1668 if (cachefd < 0)
1669 {
1670 logf("master file open failed");
1671 return ;
1672 }
1673
1674 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, tag_filename);
1675 filenametag_fd = open(buf, O_RDONLY);
1676
1677 if (filenametag_fd >= 0)
1678 {
1679 if (read(filenametag_fd, &header, sizeof(struct tagcache_header)) !=
1680 sizeof(struct tagcache_header) || header.magic != TAGCACHE_MAGIC)
1681 {
1682 logf("header error");
1683 close(filenametag_fd);
1684 filenametag_fd = -1;
1685 }
1686 }
1687
1688
1689 cpu_boost(true);
1690
1691 /* Scan for new files. */
1692 memset(&header, 0, sizeof(struct tagcache_header));
1693 write(cachefd, &header, sizeof(struct tagcache_header));
1694
1695 //strcpy(curpath, "/Best");
1696 ret = check_dir("/");
1697
1698 /* Write the header. */
1699 header.magic = TAGCACHE_MAGIC;
1700 header.datasize = data_size;
1701 header.entry_count = total_entry_count;
1702 lseek(cachefd, 0, SEEK_SET);
1703 write(cachefd, &header, sizeof(struct tagcache_header));
1704 close(cachefd);
1705
1706 if (filenametag_fd >= 0)
1707 {
1708 close(filenametag_fd);
1709 filenametag_fd = -1;
1710 }
1711
1712 if (!ret)
1713 {
1714 logf("Aborted.");
1715 cpu_boost(false);
1716 return ;
1717 }
1718
1719 /* Commit changes to the database. */
1720 if (commit())
1721 {
1722 remove(TAGCACHE_FILE_TEMP);
1723 logf("tagcache built!");
1724 }
1725
1726 cpu_boost(false);
1727}
1728
1729#ifdef HAVE_TC_RAMCACHE
1730static void load_ramcache(void)
1731{
1732 if (!hdr)
1733 return ;
1734
1735 cpu_boost(true);
1736
1737 /* At first we should load the cache (if exists). */
1738 ramcache = load_tagcache();
1739
1740 if (!ramcache)
1741 {
1742 hdr = NULL;
1743 remove_files();
1744 }
1745
1746 cpu_boost(false);
1747}
1748#endif
1749
1750static void tagcache_thread(void)
1751{
1752 struct event ev;
1753 bool check_done = false;
1754
1755 while (1)
1756 {
1757 queue_wait_w_tmo(&tagcache_queue, &ev, HZ);
1758
1759 switch (ev.id)
1760 {
1761 case Q_START_SCAN:
1762 check_done = false;
1763 break ;
1764
1765 case Q_FORCE_UPDATE:
1766 //remove_files();
1767 build_tagcache();
1768 break ;
1769
1770#ifdef HAVE_TC_RAMCACHE
1771 case SYS_TIMEOUT:
1772 if (check_done || !dircache_is_enabled())
1773 break ;
1774
1775 if (!ramcache && global_settings.tagcache_ram)
1776 load_ramcache();
1777
1778 if (global_settings.tagcache_ram)
1779 build_tagcache();
1780
1781 check_done = true;
1782 break ;
1783#endif
1784
1785 case Q_STOP_SCAN:
1786 break ;
1787
1788 case SYS_POWEROFF:
1789 break ;
1790
1791#ifndef SIMULATOR
1792 case SYS_USB_CONNECTED:
1793 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1794 usb_wait_for_disconnect(&tagcache_queue);
1795 break ;
1796#endif
1797 }
1798 }
1799}
1800
1801int tagcache_get_progress(void)
1802{
1803 int total_count = -1;
1804
1805#ifdef HAVE_DIRCACHE
1806 if (dircache_is_enabled())
1807 {
1808 total_count = dircache_get_entry_count();
1809 }
1810 else
1811 {
1812 if (hdr)
1813 total_count = hdr->h.entry_count;
1814 }
1815#endif
1816
1817 if (total_count < 0)
1818 return -1;
1819
1820 return processed_dir_count * 100 / total_count;
1821}
1822
1823void tagcache_start_scan(void)
1824{
1825 queue_post(&tagcache_queue, Q_START_SCAN, 0);
1826}
1827
1828bool tagcache_force_update(void)
1829{
1830 queue_post(&tagcache_queue, Q_FORCE_UPDATE, 0);
1831 gui_syncsplash(HZ*2, true, str(LANG_TAGCACHE_FORCE_UPDATE_SPLASH));
1832
1833 return false;
1834}
1835
1836void tagcache_stop_scan(void)
1837{
1838 queue_post(&tagcache_queue, Q_STOP_SCAN, 0);
1839}
1840
1841#ifdef HAVE_TC_RAMCACHE
1842bool tagcache_is_ramcache(void)
1843{
1844 return ramcache;
1845}
1846#endif
1847
1848void tagcache_init(void)
1849{
1850 /* If the previous cache build/update was interrupted, commit
1851 * the changes first. */
1852 cpu_boost(true);
1853 allocate_tempbuf();
1854 commit();
1855 free_tempbuf();
1856 cpu_boost(false);
1857
1858#ifdef HAVE_TC_RAMCACHE
1859 /* Allocate space for the tagcache if found on disk. */
1860 allocate_tagcache();
1861#endif
1862
1863 queue_init(&tagcache_queue);
1864 create_thread(tagcache_thread, tagcache_stack,
1865 sizeof(tagcache_stack), tagcache_thread_name);
1866}
1867
1868