7 #include "workshop_types.h"
10 #include "addon_cache.c"
12 static u32
addon_count( enum workshop_file_type type
){
13 return addon_system
.registry_type_counts
[ type
];
16 /* these kind of suck, oh well. */
17 static addon_reg
*get_addon_from_index(enum workshop_file_type type
, u32 index
){
19 for( u32 i
=0; count
<addon_count(type
); i
++ ){
20 addon_reg
*reg
= &addon_system
.registry
[i
];
21 if( reg
->type
== type
){
32 static u32
get_index_from_addon( enum workshop_file_type type
, addon_reg
*a
){
34 for( u32 i
=0; count
<addon_system
.registry_type_counts
[type
]; i
++ ){
35 addon_reg
*reg
= &addon_system
.registry
[i
];
36 if( reg
->type
== type
){
47 static u32
addon_match( enum workshop_file_type type
,
48 u64 workshop_id
, const char *foldername
){
49 u32 foldername_djb2
= vg_strdjb2( foldername
);
52 for( u32 i
=0; count
<addon_system
.registry_type_counts
[type
]; i
++ ){
53 addon_reg
*reg
= &addon_system
.registry
[i
];
54 if( reg
->type
== type
){
57 if( workshop_id
== reg
->workshop_id
)
61 if( reg
->foldername_hash
== foldername_djb2
){
62 if( !strcmp( reg
->foldername
, foldername
) ){
75 static void addon_system_init( void ){
76 u32 reg_size
= sizeof(addon_reg
)*ADDON_MOUNTED_MAX
;
77 addon_system
.registry
= vg_linear_alloc( vg_mem
.rtmemory
, reg_size
);
82 * -----------------------------------------------------------------------------
86 * Reciever for scan completion. copies the registry counts back into main fred
88 VG_STATIC
void async_addon_reg_update( void *data
, u32 size
)
90 vg_info( "Registry update notify\n" );
92 for( u32 i
=0; i
<k_workshop_file_type_max
; i
++ ){
93 addon_system
.registry_type_counts
[i
] = 0;
96 for( u32 i
=0; i
<addon_system
.registry_count
; i
++ ){
97 addon_system
.registry_type_counts
[ addon_system
.registry
[i
].type
] ++;
101 VG_STATIC
void addon_set_foldername( addon_reg
*reg
, const char name
[64] ){
102 vg_strncpy( name
, reg
->foldername
, 64, k_strncpy_always_add_null
);
103 reg
->foldername_hash
= vg_strdjb2( reg
->foldername
);
107 * Create a new registry
109 VG_STATIC addon_reg
*addon_alloc_reg( PublishedFileId_t workshop_id
,
110 enum workshop_file_type type
){
111 if( addon_system
.registry_count
== ADDON_MOUNTED_MAX
){
112 vg_error( "You have too many addons installed!\n" );
116 addon_reg
*reg
= &addon_system
.registry
[ addon_system
.registry_count
];
117 reg
->metadata_len
= 0;
118 reg
->userdata
= NULL
;
119 reg
->state
= k_addon_state_indexed
;
120 reg
->workshop_id
= workshop_id
;
121 reg
->foldername
[0] = '\0';
126 snprintf( foldername
, 64, PRINTF_U64
, workshop_id
);
127 addon_set_foldername( reg
, foldername
);
133 * If the addon.inf exists int the folder, load into the reg
135 VG_STATIC
int addon_try_load_metadata( addon_reg
*reg
, vg_str folder_path
){
136 vg_str meta_path
= folder_path
;
137 vg_strcat( &meta_path
, "/addon.inf" );
138 if( !vg_strgood( &meta_path
) ){
139 vg_error( "The metadata path is too long\n" );
143 FILE *fp
= fopen( meta_path
.buffer
, "rb" );
145 vg_error( "Could not open the '%s'\n", meta_path
.buffer
);
149 reg
->metadata_len
= fread( reg
->metadata
, 1, 512, fp
);
150 if( reg
->metadata_len
!= 512 ){
153 vg_error( "unknown error codition" );
154 reg
->metadata_len
= 0;
162 VG_STATIC
void addon_print_info( addon_reg
*reg
){
163 vg_info( "addon_reg #%u{\n", addon_system
.registry_count
);
164 vg_info( " type: %d\n", reg
->type
);
165 vg_info( " workshop_id: " PRINTF_U64
"\n", reg
->workshop_id
);
166 vg_info( " folder: [%u]%s\n", reg
->foldername_hash
, reg
->foldername
);
167 vg_info( " metadata_len: %u\n", reg
->metadata_len
);
168 vg_info( " userdata: %p\n", reg
->userdata
);
172 VG_STATIC
void addon_mount_finish( addon_reg
*reg
){
173 addon_system
.registry_count
++;
177 * Mount a fully packaged addon, one that certainly has a addon.inf
179 VG_STATIC addon_reg
*addon_mount_workshop_folder( PublishedFileId_t workshop_id
,
182 addon_reg
*reg
= addon_alloc_reg( workshop_id
, k_workshop_file_type_none
);
183 if( !reg
) return NULL
;
185 if( !addon_try_load_metadata( reg
, folder_path
) ){
189 enum workshop_file_type type
= k_workshop_file_type_none
;
191 root
.buf
= reg
->metadata
;
192 root
.len
= reg
->metadata_len
;
193 root
.max
= sizeof(reg
->metadata
);
195 vg_msg workshop
= root
;
196 if( vg_msg_seekframe( &workshop
, "workshop", k_vg_msg_first
)){
197 type
= vg_msg_seekkvu32( &workshop
, "type", k_vg_msg_first
);
200 if( type
== k_workshop_file_type_none
){
201 vg_error( "Cannot determine addon type\n" );
206 addon_mount_finish( reg
);
211 * Mount a local folder. may or may not have addon.inf
213 VG_STATIC addon_reg
*addon_mount_local_addon( const char *folder
,
214 enum workshop_file_type type
,
215 const char *content_ext
)
217 char folder_path_buf
[4096];
219 vg_strnull( &folder_path
, folder_path_buf
, 4096 );
220 vg_strcat( &folder_path
, folder
);
222 const char *folder_name
= vg_strch( &folder_path
, '/' )+1;
223 u32 folder_hash
= vg_strdjb2(folder_name
);
224 for( u32 i
=0; i
<addon_system
.registry_count
; i
++ ){
225 addon_reg
*reg
= &addon_system
.registry
[i
];
227 if( (reg
->type
== type
) && (reg
->foldername_hash
== folder_hash
) ){
228 if( !strcmp( reg
->foldername
, folder_name
) ){
229 reg
->state
= k_addon_state_indexed
;
235 addon_reg
*reg
= addon_alloc_reg( 0, type
);
236 if( !reg
) return NULL
;
237 addon_set_foldername( reg
, folder_name
);
238 addon_try_load_metadata( reg
, folder_path
);
240 if( reg
->metadata_len
== 0 ){
241 /* create our own content commands */
243 msg
.buf
= reg
->metadata
;
245 msg
.max
= sizeof(reg
->metadata
);
247 u32 content_count
= 0;
249 vg_strcat( &folder_path
, "" );
250 vg_warn( "Creating own metadata for: %s\n", folder_path
.buffer
);
253 if( !vg_dir_open(&subdir
, folder_path
.buffer
) ){
254 vg_error( "Failed to open '%s'\n", folder_path
.buffer
);
258 while( vg_dir_next_entry(&subdir
) ){
259 if( vg_dir_entry_type(&subdir
) == k_vg_entry_type_file
){
260 const char *fname
= vg_dir_entry_name(&subdir
);
261 vg_str file
= folder_path
;
262 vg_strcat( &file
, "/" );
263 vg_strcat( &file
, fname
);
264 if( !vg_strgood( &file
) ) continue;
266 char *ext
= vg_strch( &file
, '.' );
268 if( strcmp(ext
,content_ext
) ) continue;
270 vg_msg_wkvstr( &msg
, "content", fname
);
274 vg_dir_close(&subdir
);
276 if( !content_count
) return NULL
;
277 if( msg
.error
== k_vg_msg_error_OK
)
278 reg
->metadata_len
= msg
.cur
;
280 vg_error( "Error creating metadata: %d\n", msg
.error
);
285 addon_mount_finish( reg
);
290 * Check all subscribed items
292 VG_STATIC
void addon_mount_workshop_items(void){
293 if( !steam_ready
) return;
295 * Steam workshop scan
297 vg_info( "Mounting steam workshop subscriptions\n" );
298 PublishedFileId_t workshop_ids
[ ADDON_MOUNTED_MAX
];
299 u32 workshop_count
= ADDON_MOUNTED_MAX
;
301 vg_async_item
*call
= vg_async_alloc(
302 sizeof(struct async_workshop_installed_files_info
));
303 struct async_workshop_installed_files_info
*info
= call
->payload
;
304 info
->buffer
= workshop_ids
;
305 info
->len
= &workshop_count
;
306 vg_async_dispatch( call
, async_workshop_get_installed_files
);
309 for( u32 j
=0; j
<workshop_count
; j
++ ){
310 /* check for existance in both our caches
311 * ----------------------------------------------------------*/
312 PublishedFileId_t id
= workshop_ids
[j
];
313 for( u32 i
=0; i
<addon_system
.registry_count
; i
++ ){
314 addon_reg
*reg
= &addon_system
.registry
[i
];
316 if( reg
->workshop_id
== id
){
317 reg
->state
= k_addon_state_indexed
;
318 goto next_file_workshop
;
322 vg_async_item
*call1
=
323 vg_async_alloc( sizeof(struct async_workshop_filepath_info
) );
327 struct async_workshop_filepath_info
*info
= call1
->payload
;
330 info
->len
= vg_list_size(path
);
331 vg_async_dispatch( call1
, async_workshop_get_filepath
);
332 vg_async_stall(); /* too bad! */
334 vg_str folder
= {.buffer
= path
, .i
=strlen(path
), .len
=4096};
335 addon_mount_workshop_folder( id
, folder
);
341 * Scan a local content folder for addons. It must find at least one file with
342 * the specified content_ext to be considered.
344 VG_STATIC
void addon_mount_content_folder( enum workshop_file_type type
,
345 const char *base_folder
,
346 const char *content_ext
)
348 vg_info( "Mounting addons(type:%d) matching skaterift/%s/*/*%s\n",
349 type
, base_folder
, content_ext
);
353 vg_strnull( &path
, path_buf
, 4096 );
354 vg_strcat( &path
, base_folder
);
357 if( !vg_dir_open(&dir
,path
.buffer
) ){
358 vg_error( "vg_dir_open('%s') failed\n", path
.buffer
);
359 vg_async_call( workshop_async_any_complete
, NULL
, 0 );
363 vg_strcat(&path
,"/");
365 while( vg_dir_next_entry(&dir
) ){
366 if( vg_dir_entry_type(&dir
) == k_vg_entry_type_dir
){
367 const char *d_name
= vg_dir_entry_name(&dir
);
369 vg_str folder
= path
;
370 if( strlen( d_name
) > ADDON_FOLDERNAME_MAX
){
371 vg_warn( "folder too long: %s\n", d_name
);
375 vg_strcat( &folder
, d_name
);
376 if( !vg_strgood( &folder
) ) continue;
378 addon_mount_local_addon( folder
.buffer
, type
, content_ext
);
384 static int addon_get_content_folder( addon_reg
*reg
, vg_str
*folder
){
385 if( reg
->workshop_id
){
386 vg_async_item
*call
=
387 vg_async_alloc( sizeof(struct async_workshop_filepath_info
) );
388 struct async_workshop_filepath_info
*info
= call
->payload
;
389 info
->buf
= folder
->buffer
;
390 info
->id
= reg
->workshop_id
;
391 info
->len
= folder
->len
;
392 vg_async_dispatch( call
, async_workshop_get_filepath
);
393 vg_async_stall(); /* too bad! */
394 if( info
->buf
[0] == '\0' ){
395 vg_error( "Failed SteamAPI_GetItemInstallInfo(" PRINTF_U64
")\n",
399 folder
->i
= strlen( folder
->buffer
);
404 if( reg
->type
== k_workshop_file_type_board
)
405 vg_strcat( folder
, "boards/" );
406 else if( reg
->type
== k_workshop_file_type_world
)
407 vg_strcat( folder
, "maps/" );
410 vg_strcat( folder
, reg
->foldername
);