vg_msg api change
[carveJwlIkooP6JGAAIwe30JlM.git] / addon.c
1 #ifndef ADDON_C
2 #define ADDON_C
3
4 #include "addon.h"
5 #include "addon_types.h"
6 #include "vg/vg_msg.h"
7 #include "steam.h"
8 #include "workshop.h"
9
10 static u32 addon_count( enum addon_type type ){
11 return addon_system.registry_type_counts[ type ];
12 }
13
14 /* these kind of suck, oh well. */
15 static addon_reg *get_addon_from_index(enum addon_type type, u32 index){
16 u32 count = 0;
17 for( u32 i=0; count<addon_count(type); i++ ){
18 addon_reg *reg = &addon_system.registry[i];
19 if( reg->alias.type == type ){
20 if( index == count )
21 return reg;
22
23 count ++;
24 }
25 }
26
27 return NULL;
28 }
29
30 static u32 get_index_from_addon( enum addon_type type, addon_reg *a ){
31 u32 count = 0;
32 for( u32 i=0; count<addon_system.registry_type_counts[type]; i++ ){
33 addon_reg *reg = &addon_system.registry[i];
34 if( reg->alias.type == type ){
35 if( reg == a )
36 return count;
37
38 count ++;
39 }
40 }
41
42 return 0xffffffff;
43 }
44
45 static u32 addon_match( addon_alias *alias ){
46 if( alias->type == k_addon_type_none ) return 0xffffffff;
47
48 u32 foldername_djb2 = 0;
49 if( !alias->workshop_id )
50 foldername_djb2 = vg_strdjb2( alias->foldername );
51
52 u32 count = 0;
53 for( u32 i=0; count<addon_system.registry_type_counts[alias->type]; i++ ){
54 addon_reg *reg = &addon_system.registry[i];
55 if( reg->alias.type == alias->type ){
56
57 if( alias->workshop_id ){
58 if( alias->workshop_id == reg->alias.workshop_id )
59 return count;
60 }
61 else{
62 if( reg->foldername_hash == foldername_djb2 ){
63 if( !strcmp( reg->alias.foldername, alias->foldername ) ){
64 return count;
65 }
66 }
67 }
68
69 count ++;
70 }
71 }
72
73 return 0xffffffff;
74 }
75
76 /*
77 * Create a string version of addon alias in buf
78 */
79 static void addon_alias_uid( addon_alias *alias, char buf[ADDON_UID_MAX] ){
80 if( alias->workshop_id ){
81 snprintf( buf, 128, "sr%03d-steam-"PRINTF_U64,
82 alias->type, alias->workshop_id );
83 }
84 else {
85 snprintf( buf, 128, "sr%03d-local-%s",
86 alias->type, alias->foldername );
87 }
88 }
89
90 /*
91 * equality check
92 */
93 static int addon_alias_eq( addon_alias *a, addon_alias *b ){
94 if( a->type == b->type ){
95 if( a->workshop_id == b->workshop_id ){
96 if( a->workshop_id )
97 return 1;
98 else
99 return !strcmp( a->foldername, b->foldername );
100 }
101 else
102 return 0;
103 }
104 else return 0;
105 }
106
107 /*
108 * make alias represent NULL.
109 */
110 static void invalidate_addon_alias( addon_alias *alias ){
111 alias->type = k_addon_type_none;
112 alias->workshop_id = 0;
113 alias->foldername[0] = '\0';
114 }
115
116 /*
117 * parse uid to alias. returns 1 if successful
118 */
119 static int addon_uid_to_alias( char uid[ADDON_UID_MAX], addon_alias *alias ){
120 /* 1
121 * 01234567890123
122 * sr&&&-@@@@@-#*
123 * | | |
124 * type | id
125 * |
126 * location
127 */
128 if( strlen(uid) < 13 ){
129 invalidate_addon_alias( alias );
130 return 0;
131 }
132 if( !((uid[0] == 's') && (uid[1] == 'r')) ){
133 invalidate_addon_alias( alias );
134 return 0;
135 }
136
137 char type[4];
138 memcpy( type, uid+2, 3 );
139 type[3] = '\0';
140 alias->type = atoi(type);
141
142 char location[6];
143 memcpy( location, uid+6, 5 );
144 location[5] = '\0';
145
146 if( !strcmp(location,"steam") )
147 alias->workshop_id = atoll( uid+12 );
148 else if( !strcmp(location,"local") ){
149 alias->workshop_id = 0;
150 vg_strncpy( uid+12, alias->foldername, 64, k_strncpy_always_add_null );
151 }
152 else{
153 invalidate_addon_alias( alias );
154 return 0;
155 }
156
157 return 1;
158 }
159
160 static void addon_system_init( void ){
161 u32 reg_size = sizeof(addon_reg)*ADDON_MOUNTED_MAX;
162 addon_system.registry = vg_linear_alloc( vg_mem.rtmemory, reg_size );
163
164 for( u32 type=0; type<k_addon_type_max; type++ ){
165 struct addon_type_info *inf = &addon_type_infos[type];
166 struct addon_cache *cache = &addon_system.cache[type];
167
168 if( inf->cache_count ){
169 /* create the allocations pool */
170 u32 alloc_size = sizeof(struct addon_cache_entry)*inf->cache_count;
171 cache->allocs = vg_linear_alloc( vg_mem.rtmemory, alloc_size );
172 memset( cache->allocs, 0, alloc_size );
173
174 cache->pool.buffer = cache->allocs;
175 cache->pool.count = inf->cache_count;
176 cache->pool.stride = sizeof( struct addon_cache_entry );
177 cache->pool.offset = offsetof( struct addon_cache_entry, poolnode );
178 vg_pool_init( &cache->pool );
179
180 /* create the real memory */
181 u32 cache_size = inf->cache_stride*inf->cache_count;
182 cache->items = vg_linear_alloc( vg_mem.rtmemory, cache_size );
183 cache->stride = inf->cache_stride;
184 memset( cache->items, 0, cache_size );
185
186 for( i32 j=0; j<inf->cache_count; j++ ){
187 struct addon_cache_entry *alloc = &cache->allocs[j];
188 alloc->reg_ptr = NULL;
189 alloc->reg_index = 0xffffffff;
190 }
191 }
192 }
193 }
194
195 /*
196 * Scanning routines
197 * -----------------------------------------------------------------------------
198 */
199
200 /*
201 * Reciever for scan completion. copies the registry counts back into main fred
202 */
203 static void async_addon_reg_update( void *data, u32 size )
204 {
205 vg_info( "Registry update notify\n" );
206
207 for( u32 i=0; i<k_addon_type_max; i++ ){
208 addon_system.registry_type_counts[i] = 0;
209 }
210
211 for( u32 i=0; i<addon_system.registry_count; i++ ){
212 enum addon_type type = addon_system.registry[i].alias.type;
213 addon_system.registry_type_counts[ type ] ++;
214 }
215 }
216
217 static void addon_set_foldername( addon_reg *reg, const char name[64] ){
218 vg_strncpy( name, reg->alias.foldername, 64, k_strncpy_always_add_null );
219 reg->foldername_hash = vg_strdjb2( reg->alias.foldername );
220 }
221
222 /*
223 * Create a new registry
224 */
225 static addon_reg *addon_alloc_reg( PublishedFileId_t workshop_id,
226 enum addon_type type ){
227 if( addon_system.registry_count == ADDON_MOUNTED_MAX ){
228 vg_error( "You have too many addons installed!\n" );
229 return NULL;
230 }
231
232 addon_reg *reg = &addon_system.registry[ addon_system.registry_count ];
233 reg->metadata_len = 0;
234 reg->cache_id = 0;
235 reg->state = k_addon_state_indexed;
236 reg->alias.workshop_id = workshop_id;
237 reg->alias.foldername[0] = '\0';
238 reg->alias.type = type;
239
240 if( workshop_id ){
241 char foldername[64];
242 snprintf( foldername, 64, PRINTF_U64, workshop_id );
243 addon_set_foldername( reg, foldername );
244 }
245 return reg;
246 }
247
248 /*
249 * If the addon.inf exists int the folder, load into the reg
250 */
251 static int addon_try_load_metadata( addon_reg *reg, vg_str folder_path ){
252 vg_str meta_path = folder_path;
253 vg_strcat( &meta_path, "/addon.inf" );
254 if( !vg_strgood( &meta_path ) ){
255 vg_error( "The metadata path is too long\n" );
256 return 0;
257 }
258
259 FILE *fp = fopen( meta_path.buffer, "rb" );
260 if( !fp ){
261 vg_error( "Could not open the '%s'\n", meta_path.buffer );
262 return 0;
263 }
264
265 reg->metadata_len = fread( reg->metadata, 1, 512, fp );
266 if( reg->metadata_len != 512 ){
267 if( !feof(fp) ){
268 fclose(fp);
269 vg_error( "unknown error codition" );
270 reg->metadata_len = 0;
271 return 0;
272 }
273 }
274 fclose(fp);
275 return 1;
276 }
277
278 static void addon_print_info( addon_reg *reg ){
279 vg_info( "addon_reg #%u{\n", addon_system.registry_count );
280 vg_info( " type: %d\n", reg->alias.type );
281 vg_info( " workshop_id: " PRINTF_U64 "\n", reg->alias.workshop_id );
282 vg_info( " folder: [%u]%s\n", reg->foldername_hash, reg->alias.foldername );
283 vg_info( " metadata_len: %u\n", reg->metadata_len );
284 vg_info( " cache_id: %hu\n", reg->cache_id );
285 vg_info( "}\n" );
286 }
287
288 static void addon_mount_finish( addon_reg *reg ){
289 #if 0
290 addon_print_info( reg );
291 #endif
292 addon_system.registry_count ++;
293 }
294
295 /*
296 * Mount a fully packaged addon, one that certainly has a addon.inf
297 */
298 static addon_reg *addon_mount_workshop_folder( PublishedFileId_t workshop_id,
299 vg_str folder_path )
300 {
301 addon_reg *reg = addon_alloc_reg( workshop_id, k_addon_type_none );
302 if( !reg ) return NULL;
303
304 if( !addon_try_load_metadata( reg, folder_path ) ){
305 return NULL;
306 }
307
308 enum addon_type type = k_addon_type_none;
309 vg_msg msg;
310 vg_msg_init( &msg, reg->metadata, reg->metadata_len );
311
312 if( vg_msg_seekframe( &msg, "workshop" )){
313 type = vg_msg_getkvu32( &msg, "type", 0 );
314 }
315
316 if( type == k_addon_type_none ){
317 vg_error( "Cannot determine addon type\n" );
318 return NULL;
319 }
320
321 reg->alias.type = type;
322 addon_mount_finish( reg );
323 return reg;
324 }
325
326 /*
327 * Mount a local folder. may or may not have addon.inf
328 */
329 static addon_reg *addon_mount_local_addon( const char *folder,
330 enum addon_type type,
331 const char *content_ext )
332 {
333 char folder_path_buf[4096];
334 vg_str folder_path;
335 vg_strnull( &folder_path, folder_path_buf, 4096 );
336 vg_strcat( &folder_path, folder );
337
338 const char *folder_name = vg_strch( &folder_path, '/' )+1;
339 u32 folder_hash = vg_strdjb2(folder_name);
340 for( u32 i=0; i<addon_system.registry_count; i++ ){
341 addon_reg *reg = &addon_system.registry[i];
342
343 if( (reg->alias.type == type) && (reg->foldername_hash == folder_hash) ){
344 if( !strcmp( reg->alias.foldername, folder_name ) ){
345 reg->state = k_addon_state_indexed;
346 return NULL;
347 }
348 }
349 }
350
351 addon_reg *reg = addon_alloc_reg( 0, type );
352 if( !reg ) return NULL;
353 addon_set_foldername( reg, folder_name );
354 addon_try_load_metadata( reg, folder_path );
355
356 if( reg->metadata_len == 0 ){
357 /* create our own content commands */
358 vg_msg msg;
359 vg_msg_init( &msg, reg->metadata, sizeof(reg->metadata) );
360
361 u32 content_count = 0;
362
363 vg_strcat( &folder_path, "" );
364 vg_warn( "Creating own metadata for: %s\n", folder_path.buffer );
365
366 vg_dir subdir;
367 if( !vg_dir_open(&subdir, folder_path.buffer) ){
368 vg_error( "Failed to open '%s'\n", folder_path.buffer );
369 return NULL;
370 }
371
372 while( vg_dir_next_entry(&subdir) ){
373 if( vg_dir_entry_type(&subdir) == k_vg_entry_type_file ){
374 const char *fname = vg_dir_entry_name(&subdir);
375 vg_str file = folder_path;
376 vg_strcat( &file, "/" );
377 vg_strcat( &file, fname );
378 if( !vg_strgood( &file ) ) continue;
379
380 char *ext = vg_strch( &file, '.' );
381 if( !ext ) continue;
382 if( strcmp(ext,content_ext) ) continue;
383
384 vg_msg_wkvstr( &msg, "content", fname );
385 content_count ++;
386 }
387 }
388 vg_dir_close(&subdir);
389
390 if( !content_count ) return NULL;
391 if( msg.error == k_vg_msg_error_OK )
392 reg->metadata_len = msg.cur.co;
393 else{
394 vg_error( "Error creating metadata: %d\n", msg.error );
395 return NULL;
396 }
397 }
398
399 addon_mount_finish( reg );
400 return reg;
401 }
402
403 /*
404 * Check all subscribed items
405 */
406 static void addon_mount_workshop_items(void){
407 if( !steam_ready ) return;
408 /*
409 * Steam workshop scan
410 */
411 vg_info( "Mounting steam workshop subscriptions\n" );
412 PublishedFileId_t workshop_ids[ ADDON_MOUNTED_MAX ];
413 u32 workshop_count = ADDON_MOUNTED_MAX;
414
415 vg_async_item *call = vg_async_alloc(
416 sizeof(struct async_workshop_installed_files_info));
417 struct async_workshop_installed_files_info *info = call->payload;
418 info->buffer = workshop_ids;
419 info->len = &workshop_count;
420 vg_async_dispatch( call, async_workshop_get_installed_files );
421 vg_async_stall();
422
423 for( u32 j=0; j<workshop_count; j++ ){
424 /* check for existance in both our caches
425 * ----------------------------------------------------------*/
426 PublishedFileId_t id = workshop_ids[j];
427 for( u32 i=0; i<addon_system.registry_count; i++ ){
428 addon_reg *reg = &addon_system.registry[i];
429
430 if( reg->alias.workshop_id == id ){
431 reg->state = k_addon_state_indexed;
432 goto next_file_workshop;
433 }
434 }
435
436 vg_async_item *call1 =
437 vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
438
439 char path[ 4096 ];
440
441 struct async_workshop_filepath_info *info = call1->payload;
442 info->buf = path;
443 info->id = id;
444 info->len = vg_list_size(path);
445 vg_async_dispatch( call1, async_workshop_get_filepath );
446 vg_async_stall(); /* too bad! */
447
448 vg_str folder = {.buffer = path, .i=strlen(path), .len=4096};
449 addon_mount_workshop_folder( id, folder );
450 next_file_workshop:;
451 }
452 }
453
454 /*
455 * Scan a local content folder for addons. It must find at least one file with
456 * the specified content_ext to be considered.
457 */
458 static void addon_mount_content_folder( enum addon_type type,
459 const char *base_folder,
460 const char *content_ext )
461 {
462 vg_info( "Mounting addons(type:%d) matching skaterift/%s/*/*%s\n",
463 type, base_folder, content_ext );
464
465 char path_buf[4096];
466 vg_str path;
467 vg_strnull( &path, path_buf, 4096 );
468 vg_strcat( &path, base_folder );
469
470 vg_dir dir;
471 if( !vg_dir_open(&dir,path.buffer) ){
472 vg_error( "vg_dir_open('%s') failed\n", path.buffer );
473 return;
474 }
475
476 vg_strcat(&path,"/");
477
478 while( vg_dir_next_entry(&dir) ){
479 if( vg_dir_entry_type(&dir) == k_vg_entry_type_dir ){
480 const char *d_name = vg_dir_entry_name(&dir);
481
482 vg_str folder = path;
483 if( strlen( d_name ) > ADDON_FOLDERNAME_MAX ){
484 vg_warn( "folder too long: %s\n", d_name );
485 continue;
486 }
487
488 vg_strcat( &folder, d_name );
489 if( !vg_strgood( &folder ) ) continue;
490
491 addon_mount_local_addon( folder.buffer, type, content_ext );
492 }
493 }
494 vg_dir_close(&dir);
495 }
496
497 /*
498 * write the full path of the addon's folder into the vg_str
499 */
500 static int addon_get_content_folder( addon_reg *reg, vg_str *folder ){
501 if( reg->alias.workshop_id ){
502 vg_async_item *call =
503 vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
504 struct async_workshop_filepath_info *info = call->payload;
505 info->buf = folder->buffer;
506 info->id = reg->alias.workshop_id;
507 info->len = folder->len;
508 vg_async_dispatch( call, async_workshop_get_filepath );
509 vg_async_stall(); /* too bad! */
510 if( info->buf[0] == '\0' ){
511 vg_error( "Failed SteamAPI_GetItemInstallInfo(" PRINTF_U64 ")\n",
512 reg->alias.workshop_id );
513 return 0;
514 }
515 folder->i = strlen( folder->buffer );
516 return 1;
517 }
518 else{
519 folder->i = 0;
520
521 const char *local_folder =
522 addon_type_infos[reg->alias.type].local_content_folder;
523
524 if( !local_folder ) return 0;
525 vg_strcat( folder, local_folder );
526 vg_strcat( folder, reg->alias.foldername );
527 return 1;
528 }
529 }
530
531 /*
532 * Return existing cache id if reg_index points to a registry with its cache
533 * already set.
534 */
535 static u16 addon_cache_fetch( enum addon_type type, u32 reg_index ){
536 addon_reg *reg = NULL;
537
538 if( reg_index < addon_count( type ) ){
539 reg = get_addon_from_index( type, reg_index );
540 if( reg->cache_id )
541 return reg->cache_id;
542 }
543
544 return 0;
545 }
546
547 /*
548 * Allocate a new cache item from the pool
549 */
550 static u16 addon_cache_alloc( enum addon_type type, u32 reg_index ){
551 struct addon_cache *cache = &addon_system.cache[ type ];
552
553 u16 new_id = vg_pool_lru( &cache->pool );
554 struct addon_cache_entry *new_entry = vg_pool_item( &cache->pool, new_id );
555
556 addon_reg *reg = NULL;
557 if( reg_index < addon_count( type ) )
558 reg = get_addon_from_index( type, reg_index );
559
560 if( new_entry ){
561 if( new_entry->reg_ptr )
562 new_entry->reg_ptr->cache_id = 0;
563
564 if( reg )
565 reg->cache_id = new_id;
566
567 new_entry->reg_ptr = reg;
568 new_entry->reg_index = reg_index;
569 return new_id;
570 }
571 else{
572 vg_error( "cache full (type: %u)!\n", type );
573 return 0;
574 }
575 }
576
577 /*
578 * Get the real item data for cache id
579 */
580 static void *addon_cache_item( enum addon_type type, u16 id ){
581 if( !id ) return NULL;
582
583 struct addon_cache *cache = &addon_system.cache[type];
584 return cache->items + ((size_t)(id-1) * cache->stride);
585 }
586
587 /*
588 * Get the real item data for cache id ONLY if the item is completely loaded.
589 */
590 static void *addon_cache_item_if_loaded( enum addon_type type, u16 id ){
591 if( !id ) return NULL;
592
593 struct addon_cache *cache = &addon_system.cache[type];
594 struct addon_cache_entry *entry = vg_pool_item( &cache->pool, id );
595
596 if( entry->state == k_addon_cache_state_loaded )
597 return addon_cache_item( type, id );
598 else return NULL;
599 }
600
601 /*
602 * Updates the item state from the main thread
603 */
604 static void async_addon_setstate( void *_entry, u32 _state ){
605 addon_cache_entry *entry = _entry;
606 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
607 entry->state = _state;
608 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
609 vg_success( " loaded (%s)\n", entry->reg_ptr->alias.foldername );
610 }
611
612 /*
613 * Handles the loading of an individual item
614 */
615 static int addon_cache_load_request( enum addon_type type, u16 id,
616 addon_reg *reg, vg_str folder ){
617
618 /* load content files
619 * --------------------------------- */
620 vg_str content_path = folder;
621
622 vg_msg msg;
623 vg_msg_init( &msg, reg->metadata, reg->metadata_len );
624
625 const char *kv_content = vg_msg_getkvstr( &msg, "content" );
626 if( kv_content ){
627 vg_strcat( &content_path, "/" );
628 vg_strcat( &content_path, kv_content );
629 }
630 else{
631 vg_error( " No content paths in metadata\n" );
632 return 0;
633 }
634
635 if( !vg_strgood( &content_path ) ) {
636 vg_error( " Metadata path too long\n" );
637 return 0;
638 }
639
640 if( type == k_addon_type_board ){
641 struct player_board *board = addon_cache_item( type, id );
642 player_board_load( board, content_path.buffer );
643 return 1;
644 }
645 else if( type == k_addon_type_player ){
646 struct player_model *model = addon_cache_item( type, id );
647 player_model_load( model, content_path.buffer );
648 return 1;
649 }
650 else {
651 return 0;
652 }
653
654 return 0;
655 }
656
657 static void addon_cache_free_item( enum addon_type type, u16 id ){
658 if( type == k_addon_type_board ){
659 struct player_board *board = addon_cache_item( type, id );
660 player_board_unload( board );
661 }
662 else if( type == k_addon_type_player ){
663 struct player_model *model = addon_cache_item( type, id );
664 player_model_unload( model );
665 }
666 }
667
668 /*
669 * Goes over cache item load requests and calls the above ^
670 */
671 static void addon_cache_load_loop(void){
672 vg_info( "Running load loop\n" );
673 char path_buf[4096];
674
675 for( u32 type=0; type<k_addon_type_max; type++ ){
676 struct addon_cache *cache = &addon_system.cache[type];
677
678 for( u32 id=1; id<=cache->pool.count; id++ ){
679 addon_cache_entry *entry = vg_pool_item( &cache->pool, id );
680
681 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
682 if( entry->state == k_addon_cache_state_load_request ){
683 vg_info( "process cache load request (%u#%u, reg:%u)\n",
684 type, id, entry->reg_index );
685
686 if( entry->reg_index >= addon_count(type) ){
687 /* should maybe have a different value for this case */
688 entry->state = k_addon_cache_state_none;
689 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
690 continue;
691 }
692
693 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
694
695 /* continue with the request */
696 addon_reg *reg = get_addon_from_index( type, entry->reg_index );
697 entry->reg_ptr = reg;
698
699 vg_str folder;
700 vg_strnull( &folder, path_buf, 4096 );
701 if( addon_get_content_folder( reg, &folder ) ){
702 if( addon_cache_load_request( type, id, reg, folder ) ){
703 vg_async_call( async_addon_setstate,
704 entry, k_addon_cache_state_loaded );
705 continue;
706 }
707 }
708
709 vg_warn( "cache item did not load (%u#%u)\n", type, id );
710 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
711 entry->state = k_addon_cache_state_none;
712 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
713 }
714 else
715 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
716 }
717 }
718 }
719
720 /*
721 * Perform the cache interactions required to create a viewslot which will
722 * eventually be loaded by other parts of the system.
723 */
724 static u16 addon_cache_create_viewer( enum addon_type type, u16 reg_id ){
725 struct addon_cache *cache = &addon_system.cache[type];
726 vg_pool *pool = &cache->pool;
727
728 u16 cache_id = addon_cache_fetch( type, reg_id );
729 if( !cache_id ){
730 cache_id = addon_cache_alloc( type, reg_id );
731
732 if( cache_id ){
733 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
734 addon_cache_entry *entry = vg_pool_item( pool, cache_id );
735
736 if( entry->state == k_addon_cache_state_loaded ){
737 addon_cache_free_item( type, cache_id );
738 }
739
740 entry->state = k_addon_cache_state_load_request;
741 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
742 }
743 }
744
745 if( cache_id )
746 vg_pool_watch( pool, cache_id );
747
748 return cache_id;
749 }
750
751 static u16 addon_cache_create_viewer_from_uid( enum addon_type type,
752 char uid[ADDON_UID_MAX] ){
753 addon_alias q;
754 if( !addon_uid_to_alias( uid, &q ) ) return 0;
755 if( q.type != type ) return 0;
756
757 u32 reg_id = addon_match( &q );
758
759 if( reg_id == 0xffffffff ){
760 vg_warn( "We dont have the addon '%s' installed.\n", uid );
761 return 0;
762 }
763 else {
764 return addon_cache_create_viewer( type, reg_id );
765 }
766 }
767
768 static void addon_cache_watch( enum addon_type type, u16 cache_id ){
769 if( !cache_id ) return;
770
771 struct addon_cache *cache = &addon_system.cache[type];
772 vg_pool *pool = &cache->pool;
773 vg_pool_watch( pool, cache_id );
774 }
775
776 static void addon_cache_unwatch( enum addon_type type, u16 cache_id ){
777 if( !cache_id ) return;
778
779 struct addon_cache *cache = &addon_system.cache[type];
780 vg_pool *pool = &cache->pool;
781 vg_pool_unwatch( pool, cache_id );
782 }
783
784 #endif /* ADDON_C */