getting stuff working on windows again
authorhgn <hgodden00@gmail.com>
Fri, 26 May 2023 03:31:34 +0000 (04:31 +0100)
committerhgn <hgodden00@gmail.com>
Fri, 26 May 2023 03:31:34 +0000 (04:31 +0100)
addon.c [new file with mode: 0644]
addon.h [new file with mode: 0644]
ent_skateshop.c
ent_skateshop.h
skaterift.c
skaterift.h
workshop.c
workshop_types.h
world_load.c

diff --git a/addon.c b/addon.c
new file mode 100644 (file)
index 0000000..c2ad38c
--- /dev/null
+++ b/addon.c
@@ -0,0 +1,369 @@
+#ifndef ADDON_C
+#define ADDON_C
+
+#include "addon.h"
+#include "vg/vg_msg.h"
+#include "steam.h"
+
+static u32 addon_count( enum workshop_file_type type ){
+   return addon_system.registry_type_counts[ type ];
+}
+
+/* these kind of suck, oh well. */
+static addon_reg *get_addon_from_index(enum workshop_file_type type, u32 index){
+   u32 count = 0;
+   for( u32 i=0; count<addon_count(type); i++ ){
+      addon_reg *reg = &addon_system.registry[i];
+      if( reg->type == type ){
+         if( index == count )
+            return reg;
+
+         count ++;
+      }
+   }
+
+   return NULL;
+}
+
+static u32 get_index_from_addon( enum workshop_file_type type, addon_reg *a ){
+   u32 count = 0;
+   for( u32 i=0; count<addon_system.registry_type_counts[type]; i++ ){
+      addon_reg *reg = &addon_system.registry[i];
+      if( reg->type == type ){
+         if( reg == a )
+            return count;
+
+         count ++;
+      }
+   }
+
+   return 0xffffffff;
+}
+
+static void addon_system_init( void ){
+   u32 reg_size   = sizeof(addon_reg)*ADDON_MOUNTED_MAX;
+   addon_system.registry = vg_linear_alloc( vg_mem.rtmemory, reg_size );
+}
+
+/*
+ * Scanning routines
+ * -----------------------------------------------------------------------------
+ */
+
+/*
+ * Reciever for scan completion. copies the registry counts back into main fred
+ */
+VG_STATIC void async_addon_reg_update( void *data, u32 size )
+{
+   vg_info( "Registry update notify\n" );
+   
+   for( u32 i=0; i<k_workshop_file_type_max; i++ ){
+      addon_system.registry_type_counts[i] = 0;
+   }
+
+   for( u32 i=0; i<addon_system.registry_count; i++ ){
+      addon_system.registry_type_counts[ addon_system.registry[i].type ] ++;
+   }
+}
+
+VG_STATIC void addon_set_foldername( addon_reg *reg, const char name[64] ){
+   vg_strncpy( name, reg->foldername, 64, k_strncpy_always_add_null );
+   reg->foldername_hash = vg_strdjb2( reg->foldername );
+}
+
+/*
+ * Create a new registry 
+ */
+VG_STATIC addon_reg *addon_alloc_reg( PublishedFileId_t workshop_id,
+                                      enum workshop_file_type type ){
+   if( addon_system.registry_count == ADDON_MOUNTED_MAX ){
+      vg_error( "You have too many addons installed!\n" );
+      return NULL;
+   }
+
+   addon_reg *reg = &addon_system.registry[ addon_system.registry_count ];
+   reg->metadata_len = 0;
+   reg->userdata = NULL;
+   reg->state = k_addon_state_indexed;
+   reg->workshop_id = workshop_id;
+   reg->foldername[0] = '\0';
+   reg->type = type;
+
+   if( workshop_id ){
+      char foldername[64];
+      snprintf( foldername, 64, PRINTF_U64, workshop_id );
+      addon_set_foldername( reg, foldername );
+   }
+   return reg;
+}
+
+/*
+ * If the addon.inf exists int the folder, load into the reg
+ */
+VG_STATIC int addon_try_load_metadata( addon_reg *reg, vg_str folder_path ){
+   vg_str meta_path = folder_path;
+   vg_strcat( &meta_path, "/addon.inf" );
+   if( !vg_strgood( &meta_path ) ){
+      vg_error( "The metadata path is too long\n" );
+      return 0;
+   }
+
+   FILE *fp = fopen( meta_path.buffer, "rb" );
+   if( !fp ){
+      vg_error( "Could not open the '%s'\n", meta_path.buffer );
+      return 0;
+   }
+
+   reg->metadata_len = fread( reg->metadata, 1, 512, fp );
+   if( reg->metadata_len != 512 ){
+      if( !feof(fp) ){
+         fclose(fp);
+         vg_error( "unknown error codition" );
+         reg->metadata_len = 0;
+         return 0;
+      }
+   }
+   fclose(fp);
+   return 1;
+}
+
+VG_STATIC void addon_mount_finish( addon_reg *reg ){
+   vg_info( "addon_reg #%u{\n", addon_system.registry_count );
+   vg_info( "  type: %d\n", reg->type );
+   vg_info( "  workshop_id: " PRINTF_U64 "\n", reg->workshop_id );
+   vg_info( "  folder: [%u]%s\n", reg->foldername_hash, reg->foldername );
+   vg_info( "  metadata_len: %u\n", reg->metadata_len );
+   vg_info( "  userdata: %p\n", reg->userdata );
+   vg_info( "}\n" );
+
+   addon_system.registry_count ++;
+}
+
+/*
+ * Mount a fully packaged addon, one that certainly has a addon.inf
+ */
+VG_STATIC void addon_mount_workshop_folder( PublishedFileId_t workshop_id,
+                                            vg_str folder_path )
+{
+   addon_reg *reg = addon_alloc_reg( workshop_id, k_workshop_file_type_none );
+   if( !reg ) return;
+
+   if( !addon_try_load_metadata( reg, folder_path ) ){
+      return;
+   }
+
+   enum workshop_file_type type = k_workshop_file_type_none;
+   vg_msg msg;
+   vg_msg_init( &msg, reg->metadata, reg->metadata_len );
+   vg_msg_cmd cmd;
+   while( vg_msg_next( &msg, &cmd ) ){
+      if( (msg.depth == 1) && (cmd.code == k_vg_msg_code_frame) ){
+         if( VG_STRDJB2_EQ( "workshop", cmd.key, cmd.key_djb2 ) ){
+            u32 depth = msg.depth;
+            while( (msg.depth == depth) && vg_msg_next( &msg, &cmd ) ){
+               if( cmd.code & k_vg_msg_code_unsigned ){
+                  if( VG_STRDJB2_EQ( "type", cmd.key, cmd.key_djb2 ) ){
+                     type = cmd.value._u32;
+                  }
+               }
+            }
+         }
+      }
+   }
+
+   if( type == k_workshop_file_type_none ){
+      vg_error( "Cannot determine addon type\n" );
+      return;
+   }
+
+   reg->type = type;
+   addon_mount_finish( reg );
+}
+
+/*
+ * Check all subscribed items
+ */
+VG_STATIC void addon_mount_workshop_items(void){
+   if( !steam_ready ) return;
+   /*
+    * Steam workshop scan
+    */
+   vg_info( "Mounting steam workshop subscriptions\n" );
+   PublishedFileId_t workshop_ids[ ADDON_MOUNTED_MAX ];
+   u32 workshop_count = ADDON_MOUNTED_MAX;
+
+   vg_async_item *call = vg_async_alloc(
+                           sizeof(struct async_workshop_installed_files_info));
+   struct async_workshop_installed_files_info *info = call->payload;
+   info->buffer = workshop_ids;
+   info->len = &workshop_count;
+   vg_async_dispatch( call, async_workshop_get_installed_files );
+   vg_async_stall();
+
+   for( u32 j=0; j<workshop_count; j++ ){
+      /* check for existance in both our caches
+       * ----------------------------------------------------------*/
+      PublishedFileId_t id = workshop_ids[j];
+      for( u32 i=0; i<addon_system.registry_count; i++ ){
+         addon_reg *reg = &addon_system.registry[i];
+
+         if( reg->workshop_id == id ){
+            reg->state = k_addon_state_indexed;
+            goto next_file_workshop;
+         }
+      }
+
+      /* new one, lets find out what type it is
+       * ---------------------------------------------------------------*/
+      vg_async_item *call1 = 
+         vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
+
+      char path[ 4096 ];
+
+      struct async_workshop_filepath_info *info = call1->payload;
+      info->buf = path;
+      info->id = id;
+      info->len = vg_list_size(path);
+      vg_async_dispatch( call1, async_workshop_get_filepath );
+      vg_async_stall(); /* too bad! */
+
+      vg_str folder = {.buffer = path, .i=strlen(path), .len=4096};
+      addon_mount_workshop_folder( id, folder );
+next_file_workshop:;
+   }
+}
+
+/*
+ * Scan a local content folder for addons. It must find at least one file with 
+ * the specified content_ext to be considered.
+ */
+VG_STATIC void addon_mount_local_folder( enum workshop_file_type type,
+                                         const char *base_folder, 
+                                         const char *content_ext )
+{
+   vg_info( "Mounting addons(type:%d) matching skaterift/%s/*/*%s\n", 
+                  type, base_folder, content_ext );
+
+   char path_buf[4096];
+   vg_str path;
+   vg_strnull( &path, path_buf, 4096 );
+   vg_strcat( &path, base_folder );
+
+   vg_dir dir;
+   if( !vg_dir_open(&dir,path.buffer) ){
+      vg_error( "vg_dir_open('%s') failed\n", path.buffer );
+      vg_async_call( workshop_async_any_complete, NULL, 0 );
+      return;
+   }
+
+   vg_strcat(&path,"/");
+
+   while( vg_dir_next_entry(&dir) ){
+      if( vg_dir_entry_type(&dir) == k_vg_entry_type_dir ){
+         const char *d_name = vg_dir_entry_name(&dir);
+
+         vg_str folder = path;
+         char *folder_name = folder.buffer+folder.i;
+
+         if( strlen( d_name ) > ADDON_FOLDERNAME_MAX ){
+            vg_warn( "folder too long: %s\n", d_name );
+            continue;
+         }
+
+         vg_strcat( &folder, d_name );
+         if( !vg_strgood( &folder ) ) continue;
+
+         int present = 0;
+         u32 folder_hash = vg_strdjb2(folder_name);
+         for( u32 i=0; i<addon_system.registry_count; i++ ){
+            addon_reg *reg = &addon_system.registry[i];
+
+            if( (reg->type == type) && (reg->foldername_hash == folder_hash) ){
+               if( !strcmp( reg->foldername, folder_name ) ){
+                  reg->state = k_addon_state_indexed;
+                  present = 1;
+                  break;
+               }
+            }
+         }
+
+         if( !present ){
+            addon_reg *reg = addon_alloc_reg( 0, type );
+            if( !reg ) continue;
+            addon_set_foldername( reg, folder_name );
+            addon_try_load_metadata( reg, folder );
+
+            if( reg->metadata_len == 0 ){
+               /* create our own content commands */
+               vg_msg msg;
+               vg_msg_init( &msg, reg->metadata, sizeof(reg->metadata) );
+
+               u32 content_count = 0;
+
+               vg_strcat( &folder, "" );
+               vg_warn( "Creating own metadata for: %s\n", folder.buffer );
+
+               vg_dir subdir;
+               if( !vg_dir_open(&subdir, folder.buffer) ){
+                  vg_error( "Failed to open '%s'\n", folder.buffer );
+                  continue;
+               }
+
+               while( vg_dir_next_entry(&subdir) ){
+                  if( vg_dir_entry_type(&subdir) == k_vg_entry_type_file ){
+                     const char *fname = vg_dir_entry_name(&subdir);
+                     vg_str file = folder;
+                     vg_strcat( &file, "/" );
+                     vg_strcat( &file, fname );
+                     if( !vg_strgood( &file ) ) continue;
+
+                     char *ext = vg_strch( &file, '.' );
+                     if( !ext ) continue;
+                     if( strcmp(ext,content_ext) ) continue;
+                     
+                     vg_msg_wkvstr( &msg, "content", fname );
+                     content_count ++;
+                  }
+               }
+               vg_dir_close(&subdir);
+
+               if( !content_count ){
+                  continue;
+               }
+
+               if( msg.error == k_vg_msg_error_OK )
+                  reg->metadata_len = msg.cur;
+               else{
+                  vg_error( "Error creating metadata: %d\n", msg.error );
+                  continue;
+               }
+            }
+
+            addon_mount_finish( reg );
+         }
+      }
+   }
+   vg_dir_close(&dir);
+}
+
+#if 0
+/*
+ * Async thread which scans local files for addons, as well as scheduling 
+ * synchronous calls to the workshop
+ *
+ * Call this from in the loader thread.
+ */
+VG_STATIC void addon_system_scan_all(void){
+   for( u32 i=0; i<addon_system.registry_count; i++ ){
+      addon_reg *reg = &addon_system.registry[i];
+      reg->state = k_addon_state_indexed_absent;
+   }
+
+   if( steam_ready ) addon_mount_workshop_items();
+   
+   vg_async_call( addon_async_reg_update, NULL, 0 );
+   vg_async_stall();
+}
+#endif
+
+#endif /* ADDON_C */
diff --git a/addon.h b/addon.h
new file mode 100644 (file)
index 0000000..d28db0c
--- /dev/null
+++ b/addon.h
@@ -0,0 +1,49 @@
+#ifndef ADDON_H
+#define ADDON_H
+
+#include "workshop.h"
+#include "workshop_types.h"
+#define ADDON_MOUNTED_MAX 128
+#define ADDON_FOLDERNAME_MAX 64
+
+typedef struct addon_reg addon_reg;
+struct {
+   struct addon_reg{
+      PublishedFileId_t workshop_id;
+
+      enum workshop_file_type type;
+      u8 metadata[512];  /* vg_msg buffer */
+      u32 metadata_len;
+
+      char foldername[ ADDON_FOLDERNAME_MAX ];
+      u32 foldername_hash;
+      void *userdata;
+
+      enum addon_state{
+         k_addon_state_none,
+         k_addon_state_indexed,
+         k_addon_state_indexed_absent /* gone but not forgotten */
+      }
+      state;
+   }
+   *registry;
+   u32 registry_count;
+
+   /* deffered: updates in main thread */
+   u32 registry_type_counts[k_workshop_file_type_max]; 
+}
+static addon_system;
+
+static void addon_system_init( void );
+static u32 addon_count( enum workshop_file_type type );
+static addon_reg *get_addon_from_index(enum workshop_file_type type, u32 index);
+static u32 get_index_from_addon( enum workshop_file_type type, addon_reg *a );
+
+/* scanning routines */
+VG_STATIC void addon_mount_local_folder( enum workshop_file_type type,
+                                         const char *base_folder, 
+                                         const char *content_ext );
+VG_STATIC void addon_mount_workshop_items(void);
+VG_STATIC void async_addon_reg_update( void *data, u32 size );
+
+#endif /* ADDON_H */
index ce0ca5d2b55236f98164197d906a469df462b681..b3e648f45aff2a7676a667c792d6d0a3b4543c26 100644 (file)
@@ -13,6 +13,7 @@
 #include "pointcloud.h"
 #include "highscores.h"
 #include "steam.h"
+#include "addon.h"
 
 /*
  * Checks string equality but does a hash check first
@@ -29,15 +30,15 @@ static inline int const_str_eq( u32 hash, const char *str, const char *cmp )
  * Get an existing cache instance, allocate a new one to be loaded, or NULL if
  * there is no space
  */
-VG_STATIC struct cache_board *skateshop_cache_fetch( u32 registry_index )
+VG_STATIC struct cache_board *skateshop_cache_fetch_board( u32 registry_index )
 {
-   struct registry_board *reg = NULL;
+   addon_reg *reg = NULL;
 
-   if( registry_index < global_skateshop.registry_count ){
-      reg = &global_skateshop.registry[ registry_index ];
+   if( registry_index < addon_count( k_workshop_file_type_board ) ){
+      reg = get_addon_from_index( k_workshop_file_type_board, registry_index );
 
-      if( reg->cache_ptr ){
-         return reg->cache_ptr;
+      if( reg->userdata ){
+         return reg->userdata;
       }
    }
 
@@ -60,13 +61,8 @@ VG_STATIC struct cache_board *skateshop_cache_fetch( u32 registry_index )
 
    if( min_board ){
       if( min_board->state == k_cache_board_state_loaded ){
-         struct registry_board *other = 
-            &global_skateshop.registry[ min_board->registry_id ];
-
-         vg_info( "Deallocating board: '%s'\n", min_board, other->foldername );
-
          player_board_unload( &min_board->board );
-         other->cache_ptr = NULL;
+         min_board->reg_ptr->userdata = NULL;
       }
 
       if( reg ){
@@ -77,7 +73,8 @@ VG_STATIC struct cache_board *skateshop_cache_fetch( u32 registry_index )
          vg_info( "Pre-allocating board (reg:%u) 'null'\n", registry_index );
       }
 
-      min_board->registry_id = registry_index;
+      min_board->reg_ptr = reg;
+      min_board->reg_index = registry_index;
       min_board->last_use_time = vg.time;
       min_board->ref_count = 0;
       min_board->state = k_cache_board_state_load_request;
@@ -92,7 +89,7 @@ VG_STATIC struct cache_board *skateshop_cache_fetch( u32 registry_index )
 
 VG_STATIC void skateshop_update_viewpage(void)
 {
-   u32 page = global_skateshop.selected_registry_id/SKATESHOP_VIEW_SLOT_MAX;
+   u32 page = global_skateshop.selected_board_id/SKATESHOP_VIEW_SLOT_MAX;
    
    for( u32 i=0; i<SKATESHOP_VIEW_SLOT_MAX; i++ ){
       struct shop_view_slot *slot = &global_skateshop.shop_view_slots[i];
@@ -100,7 +97,7 @@ VG_STATIC void skateshop_update_viewpage(void)
       
       if( slot->cache_ptr ) unwatch_cache_board( slot->cache_ptr );
 
-      slot->cache_ptr = skateshop_cache_fetch( request_id );
+      slot->cache_ptr = skateshop_cache_fetch_board( request_id );
       if( slot->cache_ptr ) watch_cache_board( slot->cache_ptr );
    }
 }
@@ -120,20 +117,18 @@ VG_STATIC void skateshop_async_board_loaded( void *payload, u32 size )
    cache_ptr->last_use_time = vg.time;
    cache_ptr->state = k_cache_board_state_loaded;
 
-   struct registry_board *reg = 
-      &global_skateshop.registry[ cache_ptr->registry_id ];
-   reg->cache_ptr = cache_ptr;
+   cache_ptr->reg_ptr->userdata = cache_ptr;
    SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
-
-   vg_success( "Async board loaded (%s)\n", reg->foldername );
+   vg_success( "Async board loaded (%s)\n", cache_ptr->reg_ptr->foldername );
 }
 
 /*
  * Thread(or subroutine of thread), for checking view slots that weve installed.
  * Load the model if a view slot wants it
  */
-VG_STATIC void workshop_visibile_load_loop_thread( void *_args )
+VG_STATIC void workshop_visibile_load_loop(void)
 {
+   vg_info( "Running load loop\n" );
    char path_buf[4096];
    vg_str folder;
 
@@ -142,7 +137,7 @@ VG_STATIC void workshop_visibile_load_loop_thread( void *_args )
 
       SDL_AtomicLock( &global_skateshop.sl_cache_access );
       if( cache_ptr->state == k_cache_board_state_load_request ){
-         if( cache_ptr->registry_id >= global_skateshop.registry_count ){
+         if( cache_ptr->reg_index >= addon_count(k_workshop_file_type_board) ){
             /* should maybe have a different value for this case */
             cache_ptr->state = k_cache_board_state_none;
             SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
@@ -152,24 +147,23 @@ VG_STATIC void workshop_visibile_load_loop_thread( void *_args )
          /* continue with the request */
          SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
 
-         struct registry_board *reg = 
-            &global_skateshop.registry[ cache_ptr->registry_id ];
+         cache_ptr->reg_ptr = get_addon_from_index( k_workshop_file_type_board,
+                                                    cache_ptr->reg_index );
 
-         if( reg->workshop_id ){
+         if( cache_ptr->reg_ptr->workshop_id ){
             vg_async_item *call = 
                vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
 
             struct async_workshop_filepath_info *info = call->payload;
             info->buf = path_buf;
-            info->id = reg->workshop_id;
+            info->id = cache_ptr->reg_ptr->workshop_id;
             info->len = vg_list_size(path_buf);
             vg_async_dispatch( call, async_workshop_get_filepath );
             vg_async_stall(); /* too bad! */
 
             if( path_buf[0] == '\0' ){
                vg_error( "Failed SteamAPI_GetItemInstallInfo(" PRINTF_U64 ")\n",
-                           reg->workshop_id );
-
+                           cache_ptr->reg_ptr->workshop_id );
                goto file_is_broken;
             }
 
@@ -180,54 +174,41 @@ VG_STATIC void workshop_visibile_load_loop_thread( void *_args )
          else{
             vg_strnull( &folder, path_buf, 4096 );
             vg_strcat( &folder, "boards/" );
-            vg_strcat( &folder, reg->foldername );
-         }
-
-         vg_str meta_path = folder;
-         vg_strcat( &meta_path, "/addon.inf" );
-
-         if( !vg_strgood( &meta_path ) ) {
-            vg_error( "Metadata path too long\n" );
-            goto file_is_broken;
-         }
-
-         u8 meta[512];
-         FILE *fp = fopen( meta_path.buffer, "rb" );
-
-         if( !fp ) goto file_is_broken;
-
-         u32 l = fread( meta, 1, 512, fp );
-         if( l != 512 ){
-            if( !feof(fp) ){
-               fclose(fp);
-               vg_error( "unknown error codition" );
-               goto file_is_broken;
-            }
+            vg_strcat( &folder, cache_ptr->reg_ptr->foldername );
          }
-         fclose(fp);
 
          /* load content files
           * --------------------------------- */
 
          vg_str content_path = folder;
 
+         int found = 0;
          vg_msg msg;
-         vg_msg_init( &msg, meta, l );
+         vg_msg_init( &msg, cache_ptr->reg_ptr->metadata, 
+                      cache_ptr->reg_ptr->metadata_len );
          vg_msg_cmd cmd;
          while( vg_msg_next( &msg, &cmd ) ){
             if( (msg.depth == 0) && (cmd.code == k_vg_msg_code_kvstring) ){
                if( VG_STRDJB2_EQ( "content", cmd.key, cmd.key_djb2 ) ){
                   vg_strcat( &content_path, "/" );
                   vg_strcat( &content_path, cmd.value._buf );
+                  found = 1;
                   break;
                }
             }
          }
+
          if( !vg_strgood( &content_path ) ) {
             vg_error( "Metadata path too long\n" );
             goto file_is_broken;
          }
 
+         if( !found ){
+            vg_error( "No content paths in metadata\n" );
+            goto file_is_broken;
+         }
+         
+         vg_info( "Load content: %s\n", content_path.buffer );
          player_board_load( &cache_ptr->board, content_path.buffer );
          vg_async_call( skateshop_async_board_loaded, cache_ptr, 0 );
          continue;
@@ -240,390 +221,36 @@ file_is_broken:;
       else
          SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
    }
-   vg_async_call( workshop_async_any_complete, NULL, 0 );
-}
-
-/*
- * op: k_workshop_op_item_scan
- * -----------------------------------------------------------------------------
- */
-
-/*
- * Reciever for scan completion. copies the registry_count back into t0
- */
-VG_STATIC void workshop_async_reg_update( void *data, u32 size )
-{
-   vg_info( "Registry update notify\n" );
-   global_skateshop.registry_count = global_skateshop.t1_registry_count;
-}
-
-VG_STATIC void workshop_steam_scan(void)
-{
-   /*
-    * Steam workshop scan
-    */
-   vg_info( "Scanning steam workshop for boards\n" );
-   PublishedFileId_t workshop_ids[ SKATESHOP_REGISTRY_MAX ];
-   u32 workshop_count = SKATESHOP_REGISTRY_MAX;
-
-   vg_async_item *call = vg_async_alloc(
-                           sizeof(struct async_workshop_installed_files_info));
-   struct async_workshop_installed_files_info *info = call->payload;
-   info->buffer = workshop_ids;
-   info->len = &workshop_count;
-   vg_async_dispatch( call, async_workshop_get_installed_files );
-   vg_async_stall();
-
-   for( u32 j=0; j<workshop_count; j++ ){
-      /* check for existance in both our caches
-       * ----------------------------------------------------------*/
-      PublishedFileId_t id = workshop_ids[j];
-      for( u32 i=0; i<global_skateshop.t1_registry_count; i++ ){
-         struct registry_board *reg = &global_skateshop.registry[i];
-
-         if( reg->workshop_id == id ){
-            reg->state = k_registry_board_state_indexed;
-            goto next_file_workshop;
-         }
-      }
-      for( u32 i=0; i<global_skateshop.t1_world_registry_count; i++ ){
-         struct registry_world *reg = &global_skateshop.world_registry[i];
-
-         if( reg->workshop_id == id ){
-            reg->state = k_registry_board_state_indexed;
-            goto next_file_workshop;
-         }
-      }
-
-      /* new one, lets find out what type it is
-       * ---------------------------------------------------------------*/
-      vg_info( "new listing from the steam workshop!: "PRINTF_U64"\n", id );
-      vg_async_item *call1 = 
-         vg_async_alloc( sizeof(struct async_workshop_filepath_info) );
-
-      char path[ 4096 ];
-
-      struct async_workshop_filepath_info *info = call1->payload;
-      info->buf = path;
-      info->id = id;
-      info->len = vg_list_size(path);
-      vg_async_dispatch( call1, async_workshop_get_filepath );
-      vg_async_stall(); /* too bad! */
-
-      vg_info( "%s\n", path );
-      vg_str folder = {.buffer = path, .i=strlen(path), .len=4096};
-      vg_str meta_path = folder;
-      vg_strcat( &meta_path, "/addon.inf" );
-      if( !vg_strgood( &meta_path ) ){
-         vg_error( "The metadata path is too long\n" );
-         goto next_file_workshop;
-      }
-
-      u8 meta[512];
-      FILE *fp = fopen( meta_path.buffer, "rb" );
-      if( !fp ){
-         vg_error( "Could not open the '%s'\n", meta_path.buffer );
-         goto next_file_workshop;
-      }
-
-      u32 l = fread( meta, 1, 512, fp );
-      if( l != 512 ){
-         if( !feof(fp) ){
-            fclose(fp);
-            vg_error( "unknown error codition" );
-            goto next_file_workshop;
-         }
-      }
-      fclose(fp);
-
-      enum workshop_file_type type = k_workshop_file_type_none;
-      vg_msg msg;
-      vg_msg_init( &msg, meta, l );
-
-      vg_msg_cmd cmd;
-      while( vg_msg_next( &msg, &cmd ) ){
-         if( (msg.depth == 1) && (cmd.code == k_vg_msg_code_frame) ){
-            if( VG_STRDJB2_EQ( "workshop", cmd.key, cmd.key_djb2 ) ){
-               u32 depth = msg.depth;
-               while( (msg.depth == depth) && vg_msg_next( &msg, &cmd ) ){
-                  if( cmd.code & k_vg_msg_code_unsigned ){
-                     if( VG_STRDJB2_EQ( "type", cmd.key, cmd.key_djb2 ) ){
-                        type = cmd.value._u32;
-                     }
-                  }
-               }
-            }
-         }
-      }
-
-      if( type == k_workshop_file_type_none ){
-         vg_error( "Cannot determine addon type\n" );
-         goto next_file_workshop;
-      }
-
-      /* now that we have the type
-       * ------------------------------------------------------------------*/
-
-      if( type == k_workshop_file_type_board ){
-         if( global_skateshop.t1_registry_count == SKATESHOP_REGISTRY_MAX ){
-            vg_error( "You have too many boards installed!\n" );
-            goto next_file_workshop;
-         }
-
-         struct registry_board *reg = &global_skateshop.registry[
-                                       global_skateshop.t1_registry_count ++ ];
-
-         reg->cache_ptr = NULL;
-         snprintf( reg->foldername, 64, PRINTF_U64, id );
-         reg->foldername_hash = vg_strdjb2( reg->foldername );
-         reg->workshop_id = id;
-         reg->state = k_registry_board_state_indexed;
-      }
-      else if( type == k_workshop_file_type_world ){
-         if( global_skateshop.t1_world_registry_count == SKATESHOP_WORLDS_MAX ){
-            vg_error( "You have too many worlds installed!\n" );
-            goto next_file_workshop;
-         }
-
-         struct registry_world *reg = &global_skateshop.world_registry[
-                                 global_skateshop.t1_world_registry_count ++ ];
-         
-         snprintf( reg->foldername, 64, PRINTF_U64, id );
-         reg->foldername_hash = vg_strdjb2( reg->foldername );
-         reg->type = k_world_load_type_workshop;
-         reg->workshop_id = id;
-         reg->state = k_registry_board_state_indexed;
-      }
-
-next_file_workshop:;
-   }
-}
-
-/*
- * Async thread which scans local files for boards, as well as scheduling 
- * synchronous calls to the workshop
- */
-VG_STATIC void workshop_scan_thread( void *_args )
-{
-   vg_linear_clear( vg_mem.scratch );
-
-   for( u32 i=0; i<global_skateshop.t1_registry_count; i++ ){
-      struct registry_board *reg = &global_skateshop.registry[i];
-      reg->state = k_registry_board_state_indexed_absent;
-   }
-
-#if 0
-   /*
-    * Local disk scan
-    */
-   vg_info( "Scanning models/boards/*.mdl\n" );
-   tinydir_dir dir;
-   tinydir_open( &dir, "models/boards" );
-
-   while( dir.has_next ){
-      tinydir_file file;
-      tinydir_readfile( &dir, &file );
-      
-      if( file.is_reg ){
-         u32 hash = vg_strdjb2( file.name );
-
-         for( u32 i=0; i<global_skateshop.t1_registry_count; i++ ){
-            struct registry_board *reg = &global_skateshop.registry[i];
-
-            if( const_str_eq( hash, file.name, reg->filename ) ){
-               reg->state = k_registry_board_state_indexed;
-               goto next_file;
-            }
-         }
-
-         if( global_skateshop.t1_registry_count == SKATESHOP_REGISTRY_MAX ){
-            vg_error( "You have too many boards installed!\n" );
-            break;
-         }
-
-         vg_info( "new listing!: %s\n", file.name );
-
-         struct registry_board *reg = 
-            &global_skateshop.registry[global_skateshop.t1_registry_count ++];
-
-         reg->cache_ptr = NULL;
-         vg_strncpy( file.name, reg->filename, 64, k_strncpy_always_add_null );
-#if 0
-         vg_strncpy( file.name, reg->workshop.title,
-                     64, k_strncpy_always_add_null );
-#endif
-         reg->filename_hash = hash;
-         reg->workshop_id = 0;
-         reg->state = k_registry_board_state_indexed;
-
-#if 0
-         reg->workshop.author = 0;
-         strcpy( reg->workshop.author_name, "custom" );
-#endif
-      }
-
-next_file: tinydir_next( &dir );
-   }
-
-   tinydir_close(&dir);
-#endif
-
-   if( steam_ready ) workshop_steam_scan();
-   
-   vg_async_call( workshop_async_reg_update, NULL, 0 );
-   vg_async_stall();
-   workshop_visibile_load_loop_thread(NULL);
 }
 
-/*
- * Asynchronous scan of local disk for items and add them to the registry
- */
-VG_STATIC void workshop_op_item_scan(void)
-{
-   skaterift_begin_op( k_workshop_op_item_scan );
-   vg_loader_start( workshop_scan_thread, NULL );
-}
-
-/*
- * op: k_async_op_world_scan
- * -----------------------------------------------------------------------------
- */
 
-/*
- * Reciever for scan completion. copies the registry_count back into t0
- */
-VG_STATIC void workshop_async_world_reg_update( void *data, u32 size )
-{
-   vg_info( "World registry update notify\n" );
-   global_skateshop.world_registry_count = 
-      global_skateshop.t1_world_registry_count;
+VG_STATIC void world_scan_thread( void *_args ){
+   addon_mount_local_folder( k_workshop_file_type_world, "maps", ".mdl" );
+   addon_mount_workshop_items();
+   vg_async_call( async_addon_reg_update, NULL, 0 );
    skaterift_end_op();
 }
 
 /*
- * Add a local world folder to the registry, it will verify existing ones are 
- * still there too.
+ * Asynchronous scan of local disk for worlds
  */
-VG_STATIC void world_scan_register_local( const char *folder_name )
-{
-   u32 hash = vg_strdjb2( folder_name );
-   for( u32 i=0; i<global_skateshop.t1_world_registry_count; i++ ){
-      struct registry_world *reg = 
-         &global_skateshop.world_registry[i];
-
-      if( const_str_eq( hash, folder_name, reg->foldername ) ){
-         reg->state = k_registry_board_state_indexed;
-         return;
-      }
-   }
-
-   if( global_skateshop.t1_world_registry_count == SKATESHOP_WORLDS_MAX ){
-      vg_error( "You have too many worlds installed!\n" );
-      return;
-   }
-
-   vg_info( "new listing!: %s\n", folder_name );
-
-   struct registry_world *reg = &global_skateshop.world_registry[
-      global_skateshop.t1_world_registry_count ++ ];
-
-   vg_strncpy( folder_name, reg->foldername, 64, k_strncpy_overflow_fatal );
-   reg->foldername_hash = hash;
-   reg->state = k_registry_board_state_indexed;
-   //reg->meta_present = 0;
-   reg->type = k_world_load_type_local;
+VG_STATIC void skateshop_op_world_scan(void){
+   skaterift_begin_op( k_async_op_world_scan );
+   vg_loader_start( world_scan_thread, NULL );
 }
 
-/*
- * Async thread which scans local files for boards, as well as scheduling 
- * synchronous calls to the workshop
- */
-VG_STATIC void world_scan_thread( void *_args )
-{
-   vg_linear_clear( vg_mem.scratch );
-
-   for( u32 i=0; i<global_skateshop.t1_world_registry_count; i++ ){
-      struct registry_world *reg = &global_skateshop.world_registry[i];
-      reg->state = k_registry_board_state_indexed_absent;
-   }
-
-   /*
-    * Local disk scan
-    */
-   vg_info( "Scanning maps/*.mdl\n" );
-
-   char path_buf[4096];
-   vg_str path;
-   vg_strnull( &path, path_buf, 4096 );
-   vg_strcat( &path, "maps/" );
-
-   DIR *dir = opendir( path.buffer );
-   if( !dir ){
-      vg_error( "opendir('maps') failed\n" );
-      vg_async_call( workshop_async_any_complete, NULL, 0 );
-      return;
-   }
-
-   struct dirent *entry;
-   while( (entry = readdir(dir)) ){
-      if( entry->d_type == DT_DIR ){
-         if( entry->d_name[0] == '.' ) continue;
-
-         vg_str folder = path;
-         char *folder_name = folder.buffer+folder.i;
-
-         if( strlen( entry->d_name ) >
-               vg_list_size(global_skateshop.world_registry[0].foldername)){
-            vg_warn( "Map folder too long: %s\n", entry->d_name );
-            continue;
-         }
-
-         vg_strcat( &folder, entry->d_name );
-         if( !vg_strgood( &folder ) ) break;
-
-         DIR *subdir = opendir( folder.buffer );
-         while( (entry = readdir(subdir)) ){
-            if( entry->d_type == DT_REG ){
-               if( entry->d_name[0] == '.' ) continue;
-
-               vg_str file = folder;
-               vg_strcat( &file, "/" );
-               vg_strcat( &file, entry->d_name );
-               if( !vg_strgood( &file ) ) continue;
-
-               char *ext = vg_strch( &file, '.' );
-               if( !ext ) continue;
-               if( strcmp(ext,".mdl") ) continue;
-               
-               vg_strcat( &folder, "" );
-               world_scan_register_local( folder_name );
-            }
-         }
-         closedir(subdir);
-      }
-   }
-   closedir(dir);
-
-   vg_async_call( workshop_async_world_reg_update, NULL, 0 );
-
-#if 0
-   tinydir_close(&dir);
-
-   if( steam_ready ) workshop_steam_scan();
-   
-   vg_async_call( workshop_async_reg_update, NULL, 0 );
+VG_STATIC void board_scan_thread( void *_args ){
+   addon_mount_local_folder( k_workshop_file_type_board, "boards", ".mdl" );
+   addon_mount_workshop_items();
+   vg_async_call( async_addon_reg_update, NULL, 0 );
    vg_async_stall();
-   workshop_visibile_load_loop_thread(NULL);
-#endif
+   workshop_visibile_load_loop();
+   skaterift_end_op();
 }
 
-/*
- * Asynchronous scan of local disk for worlds
- */
-VG_STATIC void skateshop_op_world_scan(void)
-{
-   skaterift_begin_op( k_async_op_world_scan );
-   vg_loader_start( world_scan_thread, NULL );
+VG_STATIC void skateshop_op_board_scan(void){
+   skaterift_begin_op( k_async_op_board_scan );
+   vg_loader_start( board_scan_thread, NULL );
 }
 
 /*
@@ -633,8 +260,7 @@ VG_STATIC void skateshop_op_world_scan(void)
 
 /* we can only keep using a viewslot pointer for multiple frames if we watch it
  * using this function */
-VG_STATIC void watch_cache_board( struct cache_board *ptr )
-{
+VG_STATIC void watch_cache_board( struct cache_board *ptr ){
    if( ptr->ref_count >= 32 ){
       vg_fatal_error( "dynamic board watch missmatch (limit is 32)\n" );
    }
@@ -645,8 +271,7 @@ VG_STATIC void watch_cache_board( struct cache_board *ptr )
 
 /* after this is called, the calling code only has access to the pointer for the
  * duration of the rest of the frame */
-VG_STATIC void unwatch_cache_board( struct cache_board *ptr )
-{
+VG_STATIC void unwatch_cache_board( struct cache_board *ptr ){
    if( ptr->ref_count == 0 ){
       vg_fatal_error( "dynamic board unwatch missmatch (no watchers)\n" );
    }
@@ -654,71 +279,38 @@ VG_STATIC void unwatch_cache_board( struct cache_board *ptr )
    ptr->ref_count --;
 }
 
-/*
- * Callback handler for persona state changes,
- * it sets the author names on the registries
- */
-VG_STATIC void callback_persona_statechange( CallbackMsg_t *msg )
-{
-   PersonaStateChange_t *info = (PersonaStateChange_t *)msg->m_pubParam;
-   ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
-
-#if 0
-   if( info->m_nChangeFlags & k_EPersonaChangeName ){
-      for( u32 i=0; i<global_skateshop.registry_count; i++ ){
-         struct registry_board *reg = &global_skateshop.registry[i];
-         if( reg->workshop.author == info->m_ulSteamID ){
-            const char *name = SteamAPI_ISteamFriends_GetFriendPersonaName( 
-                                    hSteamFriends, info->m_ulSteamID );
-            str_utf8_collapse( name, reg->workshop.author_name, 32 );
-         }
-      }
-   }
-#endif
-}
 
 /*
  * VG event init
  */
-VG_STATIC void skateshop_init(void)
-{
-   u32 reg_size   = sizeof(struct registry_board)*SKATESHOP_REGISTRY_MAX,
-       wreg_size  = sizeof(struct registry_world)*SKATESHOP_WORLDS_MAX,
-       cache_size = sizeof(struct cache_board)*SKATESHOP_BOARD_CACHE_MAX;
-   
-   global_skateshop.registry = vg_linear_alloc( vg_mem.rtmemory, reg_size );
-   global_skateshop.world_registry = 
-      vg_linear_alloc( vg_mem.rtmemory, wreg_size );
+VG_STATIC void skateshop_init(void){
+   u32 cache_size = sizeof(struct cache_board)*SKATESHOP_BOARD_CACHE_MAX;
    global_skateshop.cache = vg_linear_alloc( vg_mem.rtmemory, cache_size );
-
    memset( global_skateshop.cache, 0, cache_size );
 
    for( u32 i=0; i<SKATESHOP_BOARD_CACHE_MAX; i++ ){
       struct cache_board *board = &global_skateshop.cache[i];
       board->state = k_cache_board_state_none;
-      board->registry_id = 0xffffffff;
+      board->reg_ptr= NULL;
+      board->reg_index = 0xffffffff;
       board->last_use_time = -99999.9;
       board->ref_count = 0;
    }
-
-   if( steam_ready ){
-      steam_register_callback( k_iPersonaStateChange, 
-                               callback_persona_statechange );
-   }
 }
 
 VG_STATIC struct cache_board *skateshop_selected_cache_if_loaded(void)
 {
-   if( global_skateshop.registry_count > 0 ){
-      u32 reg_id = global_skateshop.selected_registry_id;
-      struct registry_board *reg = &global_skateshop.registry[ reg_id ];
+   if( addon_count(k_workshop_file_type_board) ){
+      addon_reg *reg = get_addon_from_index(k_workshop_file_type_board,
+                                            global_skateshop.selected_board_id);
 
       SDL_AtomicLock( &global_skateshop.sl_cache_access );
-      if( reg->cache_ptr && 
-         (reg->cache_ptr->state == k_cache_board_state_loaded ) )
-      {
-         SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
-         return reg->cache_ptr;
+      if( reg->userdata ){
+         struct cache_board *cache_ptr = reg->userdata;
+         if( cache_ptr->state == k_cache_board_state_loaded ){
+            SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
+            return cache_ptr;
+         }
       }
       SDL_AtomicUnlock( &global_skateshop.sl_cache_access );
    }
@@ -741,9 +333,13 @@ VG_STATIC void pointcloud_clear_async(void *_, u32 __)
 
 VG_STATIC void skateshop_preview_loader_thread( void *_data )
 {
-   struct registry_world *reg = _data;
+   addon_reg *reg = _data;
    
-   if( reg->type == k_world_load_type_local ){
+   if( reg->workshop_id ){
+      vg_error( "Workshop files unsupported\n" );
+      vg_async_call( pointcloud_clear_async, NULL, 0 );
+   }
+   else{
       char path_buf[4096];
       vg_str path;
       vg_strnull( &path, path_buf, 4096 );
@@ -778,12 +374,9 @@ VG_STATIC void skateshop_preview_loader_thread( void *_data )
          vg_async_call( pointcloud_clear_async, NULL, 0 );
       }
    }
-   else{
-      vg_async_call( pointcloud_clear_async, NULL, 0 );
-   }
 }
 
-VG_STATIC void skateshop_load_world_preview( struct registry_world *reg )
+VG_STATIC void skateshop_load_world_preview( addon_reg *reg )
 {
    skaterift_begin_op( k_async_op_world_load_preview );
    vg_loader_start( skateshop_preview_loader_thread, reg );
@@ -853,22 +446,22 @@ VG_STATIC void global_skateshop_preupdate(void)
        */
 
       if( button_down( k_srbind_mleft ) ){
-         if( global_skateshop.selected_registry_id > 0 ){
-            global_skateshop.selected_registry_id --;
+         if( global_skateshop.selected_board_id > 0 ){
+            global_skateshop.selected_board_id --;
          }
       }
 
       if( button_down( k_srbind_mright ) ){
-         if( global_skateshop.selected_registry_id+1 < 
-                  global_skateshop.registry_count )
+         if( global_skateshop.selected_board_id+1 < 
+               addon_count(k_workshop_file_type_board) )
          {
-            global_skateshop.selected_registry_id ++;
+            global_skateshop.selected_board_id ++;
          }
       }
 
       if( selected_cache && button_down( k_srbind_maccept ) ){
          vg_info( "chose board from skateshop (%u)\n", 
-                     global_skateshop.selected_registry_id );
+                     global_skateshop.selected_board_id );
 
          if( localplayer.board_view_slot ){
             unwatch_cache_board( localplayer.board_view_slot );
@@ -876,7 +469,6 @@ VG_STATIC void global_skateshop_preupdate(void)
 
          localplayer.board_view_slot = selected_cache;
          watch_cache_board( localplayer.board_view_slot );
-
          global_skateshop_exit();
          return;
       }
@@ -915,7 +507,7 @@ VG_STATIC void global_skateshop_preupdate(void)
       int browseable = 0,
           loadable = 0;
 
-      if( global_skateshop.world_registry_count &&
+      if( addon_count(k_workshop_file_type_world) &&
             ((skaterift.async_op == k_async_op_none)||
              (skaterift.async_op == k_async_op_world_load_preview))){
          gui_helper_action( axis_display_string(k_sraxis_mbrowse_h), "browse" );
@@ -940,7 +532,7 @@ VG_STATIC void global_skateshop_preupdate(void)
 
          if( button_down( k_srbind_mright ) ){
             if( global_skateshop.selected_world_id+1 < 
-                     global_skateshop.world_registry_count )
+                     addon_count(k_workshop_file_type_world) )
             {
                global_skateshop.selected_world_id ++;
                change = 1;
@@ -953,14 +545,14 @@ VG_STATIC void global_skateshop_preupdate(void)
       }
 
       if( skaterift.async_op == k_async_op_none ){
-         struct registry_world *rw = &global_skateshop.world_registry[
-            global_skateshop.selected_world_id ];
+         addon_reg *reg = get_addon_from_index( k_workshop_file_type_world,
+            global_skateshop.selected_world_id );
 
          /* automatically load in clouds */
          if( loadable && button_down( k_srbind_maccept ) ){
             vg_info( "Select world (%u)\n", 
                       global_skateshop.selected_world_id );
-            skaterift_change_world( rw->foldername );
+            skaterift_change_world( reg->foldername );
             return;
          }
          else{
@@ -970,7 +562,7 @@ VG_STATIC void global_skateshop_preupdate(void)
                {
                   global_skateshop.pointcloud_world_id = 
                      global_skateshop.selected_world_id;
-                  skateshop_load_world_preview( rw );
+                  skateshop_load_world_preview( reg );
                }
                else{
                   pointcloud_animate( k_pointcloud_anim_opening );
@@ -1034,8 +626,7 @@ VG_STATIC void skateshop_render_boardshop(void)
       xform.co[0] = -((float)i - ((float)slot_count)*0.5f)*0.45f;
       mdl_transform_mul( &mark_rack->transform, &xform, &xform );
 
-      if( slot->cache_ptr->registry_id == 
-          global_skateshop.selected_registry_id ){
+      if( slot->cache_ptr->reg_index == global_skateshop.selected_board_id ){
          selected = 1.0f;
       }
 
@@ -1084,52 +675,47 @@ fade_out:;
    mlocal[3][2] = -0.7f;
    m4x3_mul( mrack, mlocal, mmdl );
 
-   if( global_skateshop.registry_count == 0 ){
-      font3d_simple_draw( &gui.font, 0, 
-                          "Nothing installed", &main_camera, mmdl );
-   }
-   else{
+   if( addon_count(k_workshop_file_type_board) ){
       char buf[16];
       int i=0;
-      i+=highscore_intl( buf+i, global_skateshop.selected_registry_id+1, 3 );
+      i+=highscore_intl( buf+i, global_skateshop.selected_board_id+1, 3 );
       buf[i++] = '/';
-      i+=highscore_intl( buf+i, global_skateshop.registry_count, 3 );
+      i+=highscore_intl( buf+i, addon_count(k_workshop_file_type_board), 3 );
       buf[i++] = '\0';
 
       font3d_simple_draw( &gui.font, 0, buf, &main_camera, mmdl );
    }
+   else{
+      font3d_simple_draw( &gui.font, 0, 
+                          "Nothing installed", &main_camera, mmdl );
+   }
 
    struct cache_board *cache_ptr = skateshop_selected_cache_if_loaded();
    if( !cache_ptr ) return;
 
-   struct registry_board *reg = 
-      &global_skateshop.registry[cache_ptr->registry_id];
-
-#if 0
-   struct workshop_file_info *info = &reg->workshop;
+   addon_reg *reg = cache_ptr->reg_ptr;
 
    /* Skin title
     * ----------------------------------------------------------------- */
    m3x3_zero( mlocal );
    m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
-   mlocal[3][0] = -font3d_string_width( &gui.font, 0, info->title );
+   mlocal[3][0] = -font3d_string_width( &gui.font, 0, "Flubber" );
    mlocal[3][0] *= scale*0.5f;
    mlocal[3][1] = 0.1f;
    mlocal[3][2] = 0.0f;
    m4x3_mul( mtext, mlocal, mmdl );
-   font3d_simple_draw( &gui.font, 0, info->title, &main_camera, mmdl );
+   font3d_simple_draw( &gui.font, 0, "Flubber", &main_camera, mmdl );
 
    /* Author name
     * ----------------------------------------------------------------- */
    scale *= 0.4f;
    m3x3_setdiagonalv3( mlocal, (v3f){ scale, scale, thickness } );
-   mlocal[3][0] = -font3d_string_width( &gui.font, 0, info->author_name );
+   mlocal[3][0] = -font3d_string_width( &gui.font, 0, "JA" );
    mlocal[3][0] *= scale*0.5f;
    mlocal[3][1] = 0.0f;
    mlocal[3][2] = 0.0f;
    m4x3_mul( mtext, mlocal, mmdl );
-   font3d_simple_draw( &gui.font, 0, info->author_name, &main_camera, mmdl );
-#endif
+   font3d_simple_draw( &gui.font, 0, "JA", &main_camera, mmdl );
 }
 
 VG_STATIC void skateshop_render_charshop(void)
@@ -1152,19 +738,19 @@ VG_STATIC void skateshop_render_worldshop(void)
    vg_strnull( &info, buftext, 128 );
    vg_strnull( &subtext, bufsubtext, 128 );
    
-   if( global_skateshop.world_registry_count ){
-      struct registry_world *rw = &global_skateshop.world_registry[
-         global_skateshop.selected_world_id ];
+   if( addon_count(k_workshop_file_type_world) ){
+      addon_reg *reg = get_addon_from_index( k_workshop_file_type_world,
+                           global_skateshop.selected_world_id );
 
       info.i+=highscore_intl( info.buffer+info.i, 
                               global_skateshop.selected_world_id+1, 3 );
       info.buffer[info.i++] = '/';
       info.i+=highscore_intl( info.buffer+info.i, 
-                              global_skateshop.world_registry_count, 3 );
+                              addon_count(k_workshop_file_type_world), 3 );
       info.buffer[info.i++] = ' ';
       info.buffer[info.i] = '\0';
 
-      vg_strcat( &info, rw->foldername );
+      vg_strcat( &info, "AFAWJFKAW" );
       if( skaterift.async_op == k_async_op_world_loading ||
           skaterift.async_op == k_async_op_world_preloading ){
          vg_strcat( &subtext, "Loading..." );
@@ -1267,7 +853,7 @@ VG_STATIC void ent_skateshop_call( world_instance *world, ent_call *call )
       
       if( shop->type == k_skateshop_type_boardshop ){
          skateshop_update_viewpage();
-         workshop_op_item_scan();
+         skateshop_op_board_scan();
       }
       else if( shop->type == k_skateshop_type_worldshop ){
          pointcloud_animate( k_pointcloud_anim_opening );
index 3b432c168a506a8bbc9df9f6557998de2ab3c39b..ff11e62f84ff863230358d2eb0fe294b8e68a6c9 100644 (file)
@@ -6,12 +6,10 @@
 #include "player.h"
 #include "vg/vg_steam_remote_storage.h"
 #include "workshop.h"
+#include "addon.h"
 
-#define SKATESHOP_REGISTRY_MAX     64
 #define SKATESHOP_BOARD_CACHE_MAX  10
-#define SKATESHOP_WORLDS_MAX       32
 #define SKATESHOP_VIEW_SLOT_MAX    6
-#define SKATESHOP_REGISTRYID_NONE  0xffffffff
 
 struct{
    v3f look_target;
@@ -29,7 +27,8 @@ struct{
       state;
 
       struct player_board board;
-      u32 registry_id;
+      u32 reg_index;
+      addon_reg *reg_ptr;
 
       u32 ref_count;
       double last_use_time;
@@ -43,6 +42,7 @@ struct{
    }
    shop_view_slots[ SKATESHOP_VIEW_SLOT_MAX ];
 
+#if 0
    struct registry_board{
       PublishedFileId_t workshop_id;
 
@@ -64,8 +64,6 @@ struct{
    u32 t1_registry_count,
           registry_count;
 
-   u32 selected_registry_id;
-
    /* worlds */
    struct registry_world{
       PublishedFileId_t workshop_id;
@@ -81,8 +79,10 @@ struct{
    }
    *world_registry;
    u32 t1_world_registry_count,
-          world_registry_count,
-       selected_world_id,
+          world_registry_count;
+#endif
+   u32 selected_world_id,
+       selected_board_id,
        pointcloud_world_id;
 }
 static global_skateshop;
index d22846ff0fe65d57ff7b6626ec4c8b7b3477498c..c8da6ad414281a75aad8f24523bdd9ac8384721b 100644 (file)
@@ -44,6 +44,7 @@
 #include "vehicle.c"
 #include "entity.c"
 #include "workshop.c"
+#include "addon.c"
 
 static struct player_avatar localplayer_avatar;
 static struct player_model  localplayer_models[3];
@@ -126,6 +127,9 @@ VG_STATIC void vg_load(void)
 
    vg_loader_step( player_init, NULL );
    vg_loader_step( player_ragdoll_init, NULL );
+
+   /* content stuff */
+   vg_loader_step( addon_system_init, NULL );
    vg_loader_step( workshop_init, NULL );
    vg_loader_step( skateshop_init, NULL );
    
index b729bf7726074c31396cd42494af1468c553fc34..8f9e3e7596cd4871dd77ca5417f5e7f6036940c2 100644 (file)
@@ -12,11 +12,11 @@ struct{
       k_async_op_world_loading,
       k_async_op_world_scan,
       k_async_op_world_load_preview,
+      k_async_op_board_scan,
+      k_async_op_board_load,
       k_workshop_form_op_loading_model,
       k_workshop_form_op_downloading_submission,
       k_workshop_form_op_publishing_update,
-      k_workshop_op_item_scan,
-      k_workshop_op_item_load
    }
    async_op;
 }
@@ -32,6 +32,10 @@ static int  skaterift_change_world_command( int argc, const char *argv[] );
  */
 VG_STATIC void skaterift_begin_op( enum async_operation op )
 {
+   if( vg_thread_purpose() == k_thread_purpose_loader ){
+      vg_fatal_error( "Can't do that here\n" );
+   }
+
    if( skaterift.async_op != k_async_op_none ){
       vg_fatal_error( "Async executing op(%d), tried to start op(%d)\n", 
                        skaterift.async_op, op );
@@ -54,13 +58,23 @@ VG_STATIC void skaterift_shift_op( enum async_operation op )
    vg_info( "Shifting to op( %d )\n", op );
 }
 
+VG_STATIC void skaterift_end_op(void);
+VG_STATIC void async_skaterift_end_op( void *data, u32 size ){
+   skaterift_end_op();
+}
+
 /*
  * Finished operation, otheres can now run
  */
 VG_STATIC void skaterift_end_op(void)
 {
-   vg_info( "Finishing op( %d )\n", skaterift.async_op );
-   skaterift.async_op = k_async_op_none;
+   if( vg_thread_purpose() == k_thread_purpose_loader ){
+      vg_async_call( async_skaterift_end_op, NULL, 0 );
+   }
+   else{
+      vg_info( "Finishing op( %d )\n", skaterift.async_op );
+      skaterift.async_op = k_async_op_none;
+   }
 }
 
 #endif /* SKATERIFT_H */
index baa537ce5a8bca4dbf361c8ae90c9381d28c8ac4..a2fe1fe5b36bc29bf32f8a00713842a57fbe4f48 100644 (file)
@@ -428,32 +428,32 @@ VG_STATIC void _workshop_form_submit_thread( void *data )
    descriptor_str[descriptor.cur*2] = '\0';
    vg_info( "binstr: %s\n", descriptor_str );
 
-   DIR *dir = opendir( folder.buffer );
-   if( !dir ){
+   vg_dir dir;
+   if( !vg_dir_open( &dir, folder.buffer ) ){
       vg_error( "could not open addon folder '%s'\n", folder.buffer );
       vg_async_call( workshop_async_any_complete, NULL, 0 );
       return;
    }
 
-   struct dirent *entry;
-   while( (entry = readdir(dir)) ){
-      if( entry->d_type == DT_REG ){
-         if( entry->d_name[0] == '.' ) continue;
+   while( vg_dir_next_entry(&dir) ){
+      if( vg_dir_entry_type(&dir) == k_vg_entry_type_file ){
+         const char *d_name = vg_dir_entry_name(&dir);
+         if( d_name[0] == '.' ) continue;
 
          vg_str file = folder;
          vg_strcat( &file, "/" );
-         vg_strcat( &file, entry->d_name );
+         vg_strcat( &file, d_name );
          if( !vg_strgood( &file ) ) continue;
 
          char *ext = vg_strch( &file, '.' );
          if( !ext ) continue;
          if( strcmp(ext,".mdl") ) continue;
 
-         vg_msg_wkvstr( &descriptor, "content", entry->d_name );
+         vg_msg_wkvstr( &descriptor, "content", d_name );
          break;
       }
    }
-   closedir(dir);
+   vg_dir_close(&dir);
 
    vg_str descriptor_file = folder;
    vg_strcat( &descriptor_file, "/addon.inf" );
@@ -578,8 +578,8 @@ VG_STATIC void _workshop_form_load_thread( void *data )
       return;
    }
 
-   DIR *dir = opendir( folder.buffer );
-   if( !dir ){
+   vg_dir dir;
+   if( !vg_dir_open( &dir, folder.buffer ) ){
       vg_error( "workshop async load failed: could not open folder\n" );
       vg_async_call( workshop_form_loadmodel_async_error, NULL, 0 );
       return;
@@ -588,14 +588,14 @@ VG_STATIC void _workshop_form_load_thread( void *data )
    vg_info( "Searching %s for model files\n", folder.buffer );
 
    int found_mdl = 0;
-   struct dirent *entry;
-   while( (entry = readdir(dir)) ){
-      if( entry->d_type == DT_REG ){
-         if( entry->d_name[0] == '.' ) continue;
+   while( vg_dir_next_entry(&dir) ){
+      if( vg_dir_entry_type(&dir) == k_vg_entry_type_file ){
+         const char *d_name = vg_dir_entry_name(&dir);
+         if( d_name[0] == '.' ) continue;
 
          vg_str file = folder;
          vg_strcat( &file, "/" );
-         vg_strcat( &file, entry->d_name );
+         vg_strcat( &file, d_name );
          if( !vg_strgood( &file ) ) continue;
 
          char *ext = vg_strch( &file, '.' );
@@ -605,7 +605,7 @@ VG_STATIC void _workshop_form_load_thread( void *data )
          break;
       }
    }
-   closedir(dir);
+   vg_dir_close(&dir);
 
    if( !found_mdl ){
       vg_error( "workshop async load failed: no model files found\n" );
@@ -754,7 +754,7 @@ VG_STATIC void workshop_op_download_and_view_submission( int result_index )
 
       snprintf( workshop_form.addon_folder, 
                  vg_list_size( workshop_form.addon_folder ),
-                 "Steam Cloud (%lu)", details.m_nPublishedFileId );
+                 "Steam Cloud ("PRINTF_U64")", details.m_nPublishedFileId );
 
       workshop_form.submission.file_id = details.m_nPublishedFileId;
       workshop_form.file_intent = k_workshop_form_file_intent_keep_old;
index 2d807551e7e87e7b2a9c04f26895dcb813f39bf8..8bdd1b05c87dc32e16210ed800e57e04e08f0af8 100644 (file)
@@ -2,9 +2,10 @@
 #define WORKSHOP_TYPES_H
 
 enum workshop_file_type{
-   k_workshop_file_type_none,
-   k_workshop_file_type_board,
-   k_workshop_file_type_world
+   k_workshop_file_type_none   = 0,
+   k_workshop_file_type_board  = 1,
+   k_workshop_file_type_world  = 2,
+   k_workshop_file_type_max    = 8
 };
 
 #endif /* WORKSHOP_TYPES_H */
index d62029dbccab3b44321a6a3fb3881761dbd13f0c..4f5f8f4628fe242a7da595018420f1191690f24d 100644 (file)
@@ -131,21 +131,21 @@ static void skaterift_world_changer_thread( void *data )
    char worlds[3][4096];
    u32 i=0;
 
-   DIR *dir = opendir( folder.buffer );
-   if( !dir ){
+   vg_dir dir;
+   if( !vg_dir_open(&dir, folder.buffer) ){
       vg_error( "opendir('%s') failed\n", folder.buffer );
       vg_async_call( async_skaterift_world_loaded, NULL, 0 );
       return;
    }
 
-   struct dirent *entry;
-   while( (entry = readdir(dir)) ){
-      if( entry->d_type == DT_REG ){
-         if( entry->d_name[0] == '.' ) continue;
+   while( vg_dir_next_entry(&dir) ){
+      if( vg_dir_entry_type(&dir) == k_vg_entry_type_file ){
+         const char *d_name = vg_dir_entry_name(&dir);
+         if( d_name[0] == '.' ) continue;
 
          vg_str file = folder;
          vg_strcat( &file, "/" );
-         vg_strcat( &file, entry->d_name );
+         vg_strcat( &file, d_name );
          if( !vg_strgood( &file ) ) continue;
 
          char *ext = vg_strch( &file, '.' );
@@ -160,7 +160,7 @@ static void skaterift_world_changer_thread( void *data )
          strcpy( worlds[i++], file.buffer );
       }
    }
-   closedir(dir);
+   vg_dir_close(&dir);
 
    if( i == 0 ){
       vg_warn( "There are no .mdl files in the map folder.\n" );