8 static u32
addon_count( enum workshop_file_type type
){
9 return addon_system
.registry_type_counts
[ type
];
12 /* these kind of suck, oh well. */
13 static addon_reg
*get_addon_from_index(enum workshop_file_type type
, u32 index
){
15 for( u32 i
=0; count
<addon_count(type
); i
++ ){
16 addon_reg
*reg
= &addon_system
.registry
[i
];
17 if( reg
->type
== type
){
28 static u32
get_index_from_addon( enum workshop_file_type type
, addon_reg
*a
){
30 for( u32 i
=0; count
<addon_system
.registry_type_counts
[type
]; i
++ ){
31 addon_reg
*reg
= &addon_system
.registry
[i
];
32 if( reg
->type
== type
){
43 static void addon_system_init( void ){
44 u32 reg_size
= sizeof(addon_reg
)*ADDON_MOUNTED_MAX
;
45 addon_system
.registry
= vg_linear_alloc( vg_mem
.rtmemory
, reg_size
);
50 * -----------------------------------------------------------------------------
54 * Reciever for scan completion. copies the registry counts back into main fred
56 VG_STATIC
void async_addon_reg_update( void *data
, u32 size
)
58 vg_info( "Registry update notify\n" );
60 for( u32 i
=0; i
<k_workshop_file_type_max
; i
++ ){
61 addon_system
.registry_type_counts
[i
] = 0;
64 for( u32 i
=0; i
<addon_system
.registry_count
; i
++ ){
65 addon_system
.registry_type_counts
[ addon_system
.registry
[i
].type
] ++;
69 VG_STATIC
void addon_set_foldername( addon_reg
*reg
, const char name
[64] ){
70 vg_strncpy( name
, reg
->foldername
, 64, k_strncpy_always_add_null
);
71 reg
->foldername_hash
= vg_strdjb2( reg
->foldername
);
75 * Create a new registry
77 VG_STATIC addon_reg
*addon_alloc_reg( PublishedFileId_t workshop_id
,
78 enum workshop_file_type type
){
79 if( addon_system
.registry_count
== ADDON_MOUNTED_MAX
){
80 vg_error( "You have too many addons installed!\n" );
84 addon_reg
*reg
= &addon_system
.registry
[ addon_system
.registry_count
];
85 reg
->metadata_len
= 0;
87 reg
->state
= k_addon_state_indexed
;
88 reg
->workshop_id
= workshop_id
;
89 reg
->foldername
[0] = '\0';
94 snprintf( foldername
, 64, PRINTF_U64
, workshop_id
);
95 addon_set_foldername( reg
, foldername
);
101 * If the addon.inf exists int the folder, load into the reg
103 VG_STATIC
int addon_try_load_metadata( addon_reg
*reg
, vg_str folder_path
){
104 vg_str meta_path
= folder_path
;
105 vg_strcat( &meta_path
, "/addon.inf" );
106 if( !vg_strgood( &meta_path
) ){
107 vg_error( "The metadata path is too long\n" );
111 FILE *fp
= fopen( meta_path
.buffer
, "rb" );
113 vg_error( "Could not open the '%s'\n", meta_path
.buffer
);
117 reg
->metadata_len
= fread( reg
->metadata
, 1, 512, fp
);
118 if( reg
->metadata_len
!= 512 ){
121 vg_error( "unknown error codition" );
122 reg
->metadata_len
= 0;
130 VG_STATIC
void addon_mount_finish( addon_reg
*reg
){
131 vg_info( "addon_reg #%u{\n", addon_system
.registry_count
);
132 vg_info( " type: %d\n", reg
->type
);
133 vg_info( " workshop_id: " PRINTF_U64
"\n", reg
->workshop_id
);
134 vg_info( " folder: [%u]%s\n", reg
->foldername_hash
, reg
->foldername
);
135 vg_info( " metadata_len: %u\n", reg
->metadata_len
);
136 vg_info( " userdata: %p\n", reg
->userdata
);
139 addon_system
.registry_count
++;
143 * Mount a fully packaged addon, one that certainly has a addon.inf
145 VG_STATIC
void addon_mount_workshop_folder( PublishedFileId_t workshop_id
,
148 addon_reg
*reg
= addon_alloc_reg( workshop_id
, k_workshop_file_type_none
);
151 if( !addon_try_load_metadata( reg
, folder_path
) ){
155 /* TODO: we definitely need a different, robust interface for this
157 * msg_frame f = msg_get_frame( hello )
158 * msg_kv kv = get_kv( f, "" );
159 * if( kv.type == unsigned )
163 enum workshop_file_type type
= k_workshop_file_type_none
;
165 vg_msg_init( &msg
, reg
->metadata
, reg
->metadata_len
);
167 while( vg_msg_next( &msg
, &cmd
) ){
168 if( (msg
.depth
== 1) && (cmd
.code
== k_vg_msg_code_frame
) ){
169 if( VG_STRDJB2_EQ( "workshop", cmd
.key
, cmd
.key_djb2
) ){
170 u32 depth
= msg
.depth
;
171 while( (msg
.depth
== depth
) && vg_msg_next( &msg
, &cmd
) ){
172 if( cmd
.code
& k_vg_msg_code_unsigned
){
173 if( VG_STRDJB2_EQ( "type", cmd
.key
, cmd
.key_djb2
) ){
174 type
= cmd
.value
._u32
;
182 if( type
== k_workshop_file_type_none
){
183 vg_error( "Cannot determine addon type\n" );
188 addon_mount_finish( reg
);
192 * Check all subscribed items
194 VG_STATIC
void addon_mount_workshop_items(void){
195 if( !steam_ready
) return;
197 * Steam workshop scan
199 vg_info( "Mounting steam workshop subscriptions\n" );
200 PublishedFileId_t workshop_ids
[ ADDON_MOUNTED_MAX
];
201 u32 workshop_count
= ADDON_MOUNTED_MAX
;
203 vg_async_item
*call
= vg_async_alloc(
204 sizeof(struct async_workshop_installed_files_info
));
205 struct async_workshop_installed_files_info
*info
= call
->payload
;
206 info
->buffer
= workshop_ids
;
207 info
->len
= &workshop_count
;
208 vg_async_dispatch( call
, async_workshop_get_installed_files
);
211 for( u32 j
=0; j
<workshop_count
; j
++ ){
212 /* check for existance in both our caches
213 * ----------------------------------------------------------*/
214 PublishedFileId_t id
= workshop_ids
[j
];
215 for( u32 i
=0; i
<addon_system
.registry_count
; i
++ ){
216 addon_reg
*reg
= &addon_system
.registry
[i
];
218 if( reg
->workshop_id
== id
){
219 reg
->state
= k_addon_state_indexed
;
220 goto next_file_workshop
;
224 /* new one, lets find out what type it is
225 * ---------------------------------------------------------------*/
226 vg_async_item
*call1
=
227 vg_async_alloc( sizeof(struct async_workshop_filepath_info
) );
231 struct async_workshop_filepath_info
*info
= call1
->payload
;
234 info
->len
= vg_list_size(path
);
235 vg_async_dispatch( call1
, async_workshop_get_filepath
);
236 vg_async_stall(); /* too bad! */
238 vg_str folder
= {.buffer
= path
, .i
=strlen(path
), .len
=4096};
239 addon_mount_workshop_folder( id
, folder
);
245 * Scan a local content folder for addons. It must find at least one file with
246 * the specified content_ext to be considered.
248 VG_STATIC
void addon_mount_local_folder( enum workshop_file_type type
,
249 const char *base_folder
,
250 const char *content_ext
)
252 vg_info( "Mounting addons(type:%d) matching skaterift/%s/*/*%s\n",
253 type
, base_folder
, content_ext
);
257 vg_strnull( &path
, path_buf
, 4096 );
258 vg_strcat( &path
, base_folder
);
261 if( !vg_dir_open(&dir
,path
.buffer
) ){
262 vg_error( "vg_dir_open('%s') failed\n", path
.buffer
);
263 vg_async_call( workshop_async_any_complete
, NULL
, 0 );
267 vg_strcat(&path
,"/");
269 while( vg_dir_next_entry(&dir
) ){
270 if( vg_dir_entry_type(&dir
) == k_vg_entry_type_dir
){
271 const char *d_name
= vg_dir_entry_name(&dir
);
273 vg_str folder
= path
;
274 char *folder_name
= folder
.buffer
+folder
.i
;
276 if( strlen( d_name
) > ADDON_FOLDERNAME_MAX
){
277 vg_warn( "folder too long: %s\n", d_name
);
281 vg_strcat( &folder
, d_name
);
282 if( !vg_strgood( &folder
) ) continue;
285 u32 folder_hash
= vg_strdjb2(folder_name
);
286 for( u32 i
=0; i
<addon_system
.registry_count
; i
++ ){
287 addon_reg
*reg
= &addon_system
.registry
[i
];
289 if( (reg
->type
== type
) && (reg
->foldername_hash
== folder_hash
) ){
290 if( !strcmp( reg
->foldername
, folder_name
) ){
291 reg
->state
= k_addon_state_indexed
;
299 addon_reg
*reg
= addon_alloc_reg( 0, type
);
301 addon_set_foldername( reg
, folder_name
);
302 addon_try_load_metadata( reg
, folder
);
304 if( reg
->metadata_len
== 0 ){
305 /* create our own content commands */
307 vg_msg_init( &msg
, reg
->metadata
, sizeof(reg
->metadata
) );
309 u32 content_count
= 0;
311 vg_strcat( &folder
, "" );
312 vg_warn( "Creating own metadata for: %s\n", folder
.buffer
);
315 if( !vg_dir_open(&subdir
, folder
.buffer
) ){
316 vg_error( "Failed to open '%s'\n", folder
.buffer
);
320 while( vg_dir_next_entry(&subdir
) ){
321 if( vg_dir_entry_type(&subdir
) == k_vg_entry_type_file
){
322 const char *fname
= vg_dir_entry_name(&subdir
);
323 vg_str file
= folder
;
324 vg_strcat( &file
, "/" );
325 vg_strcat( &file
, fname
);
326 if( !vg_strgood( &file
) ) continue;
328 char *ext
= vg_strch( &file
, '.' );
330 if( strcmp(ext
,content_ext
) ) continue;
332 vg_msg_wkvstr( &msg
, "content", fname
);
336 vg_dir_close(&subdir
);
338 if( !content_count
){
342 if( msg
.error
== k_vg_msg_error_OK
)
343 reg
->metadata_len
= msg
.cur
;
345 vg_error( "Error creating metadata: %d\n", msg
.error
);
350 addon_mount_finish( reg
);
359 * Async thread which scans local files for addons, as well as scheduling
360 * synchronous calls to the workshop
362 * Call this from in the loader thread.
364 VG_STATIC
void addon_system_scan_all(void){
365 for( u32 i
=0; i
<addon_system
.registry_count
; i
++ ){
366 addon_reg
*reg
= &addon_system
.registry
[i
];
367 reg
->state
= k_addon_state_indexed_absent
;
370 if( steam_ready
) addon_mount_workshop_items();
372 vg_async_call( addon_async_reg_update
, NULL
, 0 );