diff options
author | Miika Pekkarinen <miipekk@ihme.org> | 2006-03-26 11:33:42 +0000 |
---|---|---|
committer | Miika Pekkarinen <miipekk@ihme.org> | 2006-03-26 11:33:42 +0000 |
commit | 7c4e0c8730d5b076d4db4206361bc38d5256a23f (patch) | |
tree | 43382ae25de9bfa0bbabdff7d51c32b651ad47b5 /apps/tagcache.c | |
parent | 50d40ea43409745bc828e56af5e3879ea6b48cf1 (diff) | |
download | rockbox-7c4e0c8730d5b076d4db4206361bc38d5256a23f.tar.gz rockbox-7c4e0c8730d5b076d4db4206361bc38d5256a23f.zip |
Initial version of tagcache! There are still some bugs in the engine
and much more problems with the UI.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9256 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/tagcache.c')
-rw-r--r-- | apps/tagcache.c | 1868 |
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. */ | ||
36 | extern char *audiobuf, *audiobufend; | ||
37 | |||
38 | /* Tag Cache thread. */ | ||
39 | static struct event_queue tagcache_queue; | ||
40 | static long tagcache_stack[(DEFAULT_STACK_SIZE + 0x4000)/sizeof(long)]; | ||
41 | static const char tagcache_thread_name[] = "tagcache"; | ||
42 | |||
43 | /* Previous path when scanning directory tree recursively. */ | ||
44 | static char curpath[MAX_PATH*2]; | ||
45 | static long curpath_size = sizeof(curpath); | ||
46 | |||
47 | /* Used when removing duplicates. */ | ||
48 | static char *tempbuf; /* Allocated when needed. */ | ||
49 | static long tempbufidx; /* Current location in buffer. */ | ||
50 | static long tempbuf_size; /* Buffer size (TEMPBUF_SIZE). */ | ||
51 | static long tempbuf_left; /* Buffer space left. */ | ||
52 | static long tempbuf_pos; | ||
53 | |||
54 | /* Tags we want to be unique (loaded to the tempbuf). */ | ||
55 | static 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. */ | ||
71 | struct tagfile_entry { | ||
72 | short tag_length; | ||
73 | char tag_data[0]; | ||
74 | }; | ||
75 | |||
76 | /* Fixed-size tag entry in master db index. */ | ||
77 | struct index_entry { | ||
78 | long tag_seek[TAG_COUNT]; | ||
79 | }; | ||
80 | |||
81 | /* Header is the same in every file. */ | ||
82 | struct 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. */ | ||
90 | struct 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 | |||
97 | static struct ramcache_header *hdr; | ||
98 | static bool ramcache = false; | ||
99 | static long tagcache_size = 0; | ||
100 | #endif | ||
101 | |||
102 | /** | ||
103 | * Full tag entries stored in a temporary file waiting | ||
104 | * for commit to the cache. */ | ||
105 | struct temp_file_entry { | ||
106 | long tag_offset[TAG_COUNT]; | ||
107 | short tag_length[TAG_COUNT]; | ||
108 | |||
109 | long data_length; | ||
110 | }; | ||
111 | |||
112 | struct tempbuf_id { | ||
113 | int id; | ||
114 | struct tempbuf_id *next; | ||
115 | }; | ||
116 | |||
117 | struct tempbuf_searchidx { | ||
118 | struct tempbuf_id *id; | ||
119 | char *str; | ||
120 | int seek; | ||
121 | }; | ||
122 | |||
123 | |||
124 | /* Used when building the temporary file. */ | ||
125 | static int cachefd = -1, filenametag_fd; | ||
126 | static int total_entry_count = 0; | ||
127 | static int data_size = 0; | ||
128 | static int processed_dir_count; | ||
129 | |||
130 | #ifdef HAVE_TC_RAMCACHE | ||
131 | static 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 | |||
183 | static 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 | |||
309 | static 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 | |||
417 | bool 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 | |||
464 | bool 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 | |||
477 | bool 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 | ||
577 | void 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 | |||
598 | void 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 | ||
610 | struct 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 | |||
615 | bool 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 | |||
633 | static 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 | |||
641 | inline 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 | ||
650 | static void add_tagcache(const char *path, const struct dircache_entry *dc) | ||
651 | #else | ||
652 | static 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 | |||
728 | static 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 | |||
741 | static 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 | |||
776 | static 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 | |||
808 | static 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 | |||
823 | static 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 | |||
854 | static 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 | |||
876 | static 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 | |||
887 | static 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 | |||
900 | static 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 | |||
1278 | static 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 | |||
1368 | static 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 | |||
1376 | static 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 | ||
1387 | static 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 | |||
1429 | static 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 | |||
1592 | static 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 | |||
1646 | static 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 | ||
1730 | static 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 | |||
1750 | static 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 | |||
1801 | int 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 | |||
1823 | void tagcache_start_scan(void) | ||
1824 | { | ||
1825 | queue_post(&tagcache_queue, Q_START_SCAN, 0); | ||
1826 | } | ||
1827 | |||
1828 | bool 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 | |||
1836 | void tagcache_stop_scan(void) | ||
1837 | { | ||
1838 | queue_post(&tagcache_queue, Q_STOP_SCAN, 0); | ||
1839 | } | ||
1840 | |||
1841 | #ifdef HAVE_TC_RAMCACHE | ||
1842 | bool tagcache_is_ramcache(void) | ||
1843 | { | ||
1844 | return ramcache; | ||
1845 | } | ||
1846 | #endif | ||
1847 | |||
1848 | void 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 | |||