network items, interp boundaries
[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 root = {0};
310 root.buf = reg->metadata;
311 root.len = reg->metadata_len;
312 root.max = sizeof(reg->metadata);
313
314 vg_msg workshop = root;
315 if( vg_msg_seekframe( &workshop, "workshop", k_vg_msg_first )){
316 type = vg_msg_seekkvu32( &workshop, "type", k_vg_msg_first );
317 }
318
319 if( type == k_addon_type_none ){
320 vg_error( "Cannot determine addon type\n" );
321 return NULL;
322 }
323
324 reg->alias.type = type;
325 addon_mount_finish( reg );
326 return reg;
327 }
328
329 /*
330 * Mount a local folder. may or may not have addon.inf
331 */
332 static addon_reg *addon_mount_local_addon( const char *folder,
333 enum addon_type type,
334 const char *content_ext )
335 {
336 char folder_path_buf[4096];
337 vg_str folder_path;
338 vg_strnull( &folder_path, folder_path_buf, 4096 );
339 vg_strcat( &folder_path, folder );
340
341 const char *folder_name = vg_strch( &folder_path, '/' )+1;
342 u32 folder_hash = vg_strdjb2(folder_name);
343 for( u32 i=0; i<addon_system.registry_count; i++ ){
344 addon_reg *reg = &addon_system.registry[i];
345
346 if( (reg->alias.type == type) && (reg->foldername_hash == folder_hash) ){
347 if( !strcmp( reg->alias.foldername, folder_name ) ){
348 reg->state = k_addon_state_indexed;
349 return NULL;
350 }
351 }
352 }
353
354 addon_reg *reg = addon_alloc_reg( 0, type );
355 if( !reg ) return NULL;
356 addon_set_foldername( reg, folder_name );
357 addon_try_load_metadata( reg, folder_path );
358
359 if( reg->metadata_len == 0 ){
360 /* create our own content commands */
361 vg_msg msg = {0};
362 msg.buf = reg->metadata;
363 msg.len = 0;
364 msg.max = sizeof(reg->metadata);
365
366 u32 content_count = 0;
367
368 vg_strcat( &folder_path, "" );
369 vg_warn( "Creating own metadata for: %s\n", folder_path.buffer );
370
371 vg_dir subdir;
372 if( !vg_dir_open(&subdir, folder_path.buffer) ){
373 vg_error( "Failed to open '%s'\n", folder_path.buffer );
374 return NULL;
375 }
376
377 while( vg_dir_next_entry(&subdir) ){
378 if( vg_dir_entry_type(&subdir) == k_vg_entry_type_file ){
379 const char *fname = vg_dir_entry_name(&subdir);
380 vg_str file = folder_path;
381 vg_strcat( &file, "/" );
382 vg_strcat( &file, fname );
383 if( !vg_strgood( &file ) ) continue;
384
385 char *ext = vg_strch( &file, '.' );
386 if( !ext ) continue;
387 if( strcmp(ext,content_ext) ) continue;
388
389 vg_msg_wkvstr( &msg, "content", fname );
390 content_count ++;
391 }
392 }
393 vg_dir_close(&subdir);
394
395 if( !content_count ) return NULL;
396 if( msg.error == k_vg_msg_error_OK )
397 reg->metadata_len = msg.cur;
398 else{
399 vg_error( "Error creating metadata: %d\n", msg.error );
400 return NULL;
401 }
402 }
403
404 addon_mount_finish( reg );
405 return reg;
406 }
407
408 /*
409 * Check all subscribed items
410 */
411 static void addon_mount_workshop_items(void){
412 if( !steam_ready ) return;
413 /*
414 * Steam workshop scan
415 */
416 vg_info( "Mounting steam workshop subscriptions\n" );
417 PublishedFileId_t workshop_ids[ ADDON_MOUNTED_MAX ];
418 u32 workshop_count = ADDON_MOUNTED_MAX;
419
420 vg_async_item *call = vg_async_alloc(
421 sizeof(struct async_workshop_installed_files_info));
422 struct async_workshop_installed_files_info *info = call->payload;
423 info->buffer = workshop_ids;
424 info->len = &workshop_count;
425 vg_async_dispatch( call, async_workshop_get_installed_files );
426 vg_async_stall();
427
428 for( u32 j=0; j<workshop_count; j++ ){
429 /* check for existance in both our caches
430 * ----------------------------------------------------------*/
431 PublishedFileId_t id = workshop_ids[j];
432 for( u32 i=0; i<addon_system.registry_count; i++ ){
433 addon_reg *reg = &addon_system.registry[i];
434
435 if( reg->alias.workshop_id == id ){
436 reg->state = k_addon_state_indexed;
437 goto next_file_workshop;
438 }
439 }
440
441 vg_async_item *call1 =
442 vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
443
444 char path[ 4096 ];
445
446 struct async_workshop_filepath_info *info = call1->payload;
447 info->buf = path;
448 info->id = id;
449 info->len = vg_list_size(path);
450 vg_async_dispatch( call1, async_workshop_get_filepath );
451 vg_async_stall(); /* too bad! */
452
453 vg_str folder = {.buffer = path, .i=strlen(path), .len=4096};
454 addon_mount_workshop_folder( id, folder );
455 next_file_workshop:;
456 }
457 }
458
459 /*
460 * Scan a local content folder for addons. It must find at least one file with
461 * the specified content_ext to be considered.
462 */
463 static void addon_mount_content_folder( enum addon_type type,
464 const char *base_folder,
465 const char *content_ext )
466 {
467 vg_info( "Mounting addons(type:%d) matching skaterift/%s/*/*%s\n",
468 type, base_folder, content_ext );
469
470 char path_buf[4096];
471 vg_str path;
472 vg_strnull( &path, path_buf, 4096 );
473 vg_strcat( &path, base_folder );
474
475 vg_dir dir;
476 if( !vg_dir_open(&dir,path.buffer) ){
477 vg_error( "vg_dir_open('%s') failed\n", path.buffer );
478 return;
479 }
480
481 vg_strcat(&path,"/");
482
483 while( vg_dir_next_entry(&dir) ){
484 if( vg_dir_entry_type(&dir) == k_vg_entry_type_dir ){
485 const char *d_name = vg_dir_entry_name(&dir);
486
487 vg_str folder = path;
488 if( strlen( d_name ) > ADDON_FOLDERNAME_MAX ){
489 vg_warn( "folder too long: %s\n", d_name );
490 continue;
491 }
492
493 vg_strcat( &folder, d_name );
494 if( !vg_strgood( &folder ) ) continue;
495
496 addon_mount_local_addon( folder.buffer, type, content_ext );
497 }
498 }
499 vg_dir_close(&dir);
500 }
501
502 /*
503 * write the full path of the addon's folder into the vg_str
504 */
505 static int addon_get_content_folder( addon_reg *reg, vg_str *folder ){
506 if( reg->alias.workshop_id ){
507 vg_async_item *call =
508 vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
509 struct async_workshop_filepath_info *info = call->payload;
510 info->buf = folder->buffer;
511 info->id = reg->alias.workshop_id;
512 info->len = folder->len;
513 vg_async_dispatch( call, async_workshop_get_filepath );
514 vg_async_stall(); /* too bad! */
515 if( info->buf[0] == '\0' ){
516 vg_error( "Failed SteamAPI_GetItemInstallInfo(" PRINTF_U64 ")\n",
517 reg->alias.workshop_id );
518 return 0;
519 }
520 folder->i = strlen( folder->buffer );
521 return 1;
522 }
523 else{
524 folder->i = 0;
525
526 const char *local_folder =
527 addon_type_infos[reg->alias.type].local_content_folder;
528
529 if( !local_folder ) return 0;
530 vg_strcat( folder, local_folder );
531 vg_strcat( folder, reg->alias.foldername );
532 return 1;
533 }
534 }
535
536 /*
537 * Return existing cache id if reg_index points to a registry with its cache
538 * already set.
539 */
540 static u16 addon_cache_fetch( enum addon_type type, u32 reg_index ){
541 addon_reg *reg = NULL;
542
543 if( reg_index < addon_count( type ) ){
544 reg = get_addon_from_index( type, reg_index );
545 if( reg->cache_id )
546 return reg->cache_id;
547 }
548
549 return 0;
550 }
551
552 /*
553 * Allocate a new cache item from the pool
554 */
555 static u16 addon_cache_alloc( enum addon_type type, u32 reg_index ){
556 struct addon_cache *cache = &addon_system.cache[ type ];
557
558 u16 new_id = vg_pool_lru( &cache->pool );
559 struct addon_cache_entry *new_entry = vg_pool_item( &cache->pool, new_id );
560
561 addon_reg *reg = NULL;
562 if( reg_index < addon_count( type ) )
563 reg = get_addon_from_index( type, reg_index );
564
565 if( new_entry ){
566 if( new_entry->reg_ptr )
567 new_entry->reg_ptr->cache_id = 0;
568
569 if( reg )
570 reg->cache_id = new_id;
571
572 new_entry->reg_ptr = reg;
573 new_entry->reg_index = reg_index;
574 return new_id;
575 }
576 else{
577 vg_error( "cache full (type: %u)!\n", type );
578 return 0;
579 }
580 }
581
582 /*
583 * Get the real item data for cache id
584 */
585 static void *addon_cache_item( enum addon_type type, u16 id ){
586 if( !id ) return NULL;
587
588 struct addon_cache *cache = &addon_system.cache[type];
589 return cache->items + ((size_t)(id-1) * cache->stride);
590 }
591
592 /*
593 * Get the real item data for cache id ONLY if the item is completely loaded.
594 */
595 static void *addon_cache_item_if_loaded( enum addon_type type, u16 id ){
596 if( !id ) return NULL;
597
598 struct addon_cache *cache = &addon_system.cache[type];
599 struct addon_cache_entry *entry = vg_pool_item( &cache->pool, id );
600
601 if( entry->state == k_addon_cache_state_loaded )
602 return addon_cache_item( type, id );
603 else return NULL;
604 }
605
606 /*
607 * Updates the item state from the main thread
608 */
609 static void async_addon_setstate( void *_entry, u32 _state ){
610 addon_cache_entry *entry = _entry;
611 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
612 entry->state = _state;
613 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
614 vg_success( " loaded (%s)\n", entry->reg_ptr->alias.foldername );
615 }
616
617 /*
618 * Handles the loading of an individual item
619 */
620 static int addon_cache_load_request( enum addon_type type, u16 id,
621 addon_reg *reg, vg_str folder ){
622
623 /* load content files
624 * --------------------------------- */
625 vg_str content_path = folder;
626
627 vg_msg root = {0};
628 root.buf = reg->metadata;
629 root.len = reg->metadata_len;
630 root.max = sizeof(reg->metadata);
631
632 const char *kv_content = vg_msg_seekkvstr( &root, "content", 0 );
633 if( kv_content ){
634 vg_strcat( &content_path, "/" );
635 vg_strcat( &content_path, kv_content );
636 }
637 else{
638 vg_error( " No content paths in metadata\n" );
639 return 0;
640 }
641
642 if( !vg_strgood( &content_path ) ) {
643 vg_error( " Metadata path too long\n" );
644 return 0;
645 }
646
647 if( type == k_addon_type_board ){
648 struct player_board *board = addon_cache_item( type, id );
649 player_board_load( board, content_path.buffer );
650 return 1;
651 }
652 else if( type == k_addon_type_player ){
653 struct player_model *model = addon_cache_item( type, id );
654 player_model_load( model, content_path.buffer );
655 return 1;
656 }
657 else {
658 return 0;
659 }
660
661 return 0;
662 }
663
664 static void addon_cache_free_item( enum addon_type type, u16 id ){
665 if( type == k_addon_type_board ){
666 struct player_board *board = addon_cache_item( type, id );
667 player_board_unload( board );
668 }
669 else if( type == k_addon_type_player ){
670 struct player_model *model = addon_cache_item( type, id );
671 player_model_unload( model );
672 }
673 }
674
675 /*
676 * Goes over cache item load requests and calls the above ^
677 */
678 static void addon_cache_load_loop(void){
679 vg_info( "Running load loop\n" );
680 char path_buf[4096];
681
682 for( u32 type=0; type<k_addon_type_max; type++ ){
683 struct addon_cache *cache = &addon_system.cache[type];
684
685 for( u32 id=1; id<=cache->pool.count; id++ ){
686 addon_cache_entry *entry = vg_pool_item( &cache->pool, id );
687
688 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
689 if( entry->state == k_addon_cache_state_load_request ){
690 vg_info( "process cache load request (%u#%u, reg:%u)\n",
691 type, id, entry->reg_index );
692
693 if( entry->reg_index >= addon_count(type) ){
694 /* should maybe have a different value for this case */
695 entry->state = k_addon_cache_state_none;
696 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
697 continue;
698 }
699
700 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
701
702 /* continue with the request */
703 addon_reg *reg = get_addon_from_index( type, entry->reg_index );
704 entry->reg_ptr = reg;
705
706 vg_str folder;
707 vg_strnull( &folder, path_buf, 4096 );
708 if( addon_get_content_folder( reg, &folder ) ){
709 if( addon_cache_load_request( type, id, reg, folder ) ){
710 vg_async_call( async_addon_setstate,
711 entry, k_addon_cache_state_loaded );
712 continue;
713 }
714 }
715
716 vg_warn( "cache item did not load (%u#%u)\n", type, id );
717 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
718 entry->state = k_addon_cache_state_none;
719 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
720 }
721 else
722 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
723 }
724 }
725 }
726
727 /*
728 * Perform the cache interactions required to create a viewslot which will
729 * eventually be loaded by other parts of the system.
730 */
731 static u16 addon_cache_create_viewer( enum addon_type type, u16 reg_id ){
732 struct addon_cache *cache = &addon_system.cache[type];
733 vg_pool *pool = &cache->pool;
734
735 u16 cache_id = addon_cache_fetch( type, reg_id );
736 if( !cache_id ){
737 cache_id = addon_cache_alloc( type, reg_id );
738
739 if( cache_id ){
740 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
741 addon_cache_entry *entry = vg_pool_item( pool, cache_id );
742
743 if( entry->state == k_addon_cache_state_loaded ){
744 addon_cache_free_item( type, cache_id );
745 }
746
747 entry->state = k_addon_cache_state_load_request;
748 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
749 }
750 }
751
752 if( cache_id )
753 vg_pool_watch( pool, cache_id );
754
755 return cache_id;
756 }
757
758 static u16 addon_cache_create_viewer_from_uid( enum addon_type type,
759 char uid[ADDON_UID_MAX] ){
760 addon_alias q;
761 if( !addon_uid_to_alias( uid, &q ) ) return 0;
762 if( q.type != type ) return 0;
763
764 u32 reg_id = addon_match( &q );
765
766 if( reg_id == 0xffffffff ){
767 vg_warn( "We dont have the addon '%s' installed.\n", uid );
768 return 0;
769 }
770 else {
771 return addon_cache_create_viewer( type, reg_id );
772 }
773 }
774
775 static void addon_cache_watch( enum addon_type type, u16 cache_id ){
776 if( !cache_id ) return;
777
778 struct addon_cache *cache = &addon_system.cache[type];
779 vg_pool *pool = &cache->pool;
780 vg_pool_watch( pool, cache_id );
781 }
782
783 static void addon_cache_unwatch( enum addon_type type, u16 cache_id ){
784 if( !cache_id ) return;
785
786 struct addon_cache *cache = &addon_system.cache[type];
787 vg_pool *pool = &cache->pool;
788 vg_pool_unwatch( pool, cache_id );
789 }
790
791 #endif /* ADDON_C */