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