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