void skaterift_script_write_savedata( vg_msg *sav )
 {
-   
+   for( u32 i=0; i<VG_ARRAY_LEN( _script_infos ); i ++ )
+   {
+      struct script_info *info = &_script_infos[i];
+
+      vg_msg_frame( sav, info->alias );
+      {
+         vg_msg_wkvnum( sav, "viewed_time", 
+                        k_vg_msg_u64, 1, &info->viewed_time );
+
+         u8 availible = info->availible;
+         vg_msg_wkvnum( sav, "availible", k_vg_msg_u8, 1, &availible );
+      }
+      vg_msg_end_frame( sav );
+   }
+}
+
+void skaterift_script_load_savedata( vg_msg *sav )
+{
+   vg_msg_cursor orig = sav->cur;
+
+   for( u32 i=0; i<VG_ARRAY_LEN( _script_infos ); i ++ )
+   {
+      sav->cur = orig;
+      struct script_info *info = &_script_infos[i];
+
+      if( vg_msg_seekframe( sav, info->alias ) )
+      {
+         vg_msg_getkvintg( sav, "viewed_time", k_vg_msg_u64, 
+                           &info->viewed_time, NULL );
+
+         u8 availible;
+         vg_msg_getkvintg( sav, "availible", k_vg_msg_u8, &availible, NULL );
+         info->availible = availible? 1: 0;
+      }
+   }
+}
+
+enum script_action
+{
+   k_script_action_none,
+   k_script_action_unlock,
+   k_script_action_reset,
+   k_script_action_info,
+   k_script_action_play
+};
+
+static void _skaterift_script_hook_apply_action( u32 script_id,
+                                                 enum script_action action )
+{
+   struct script_info *info = &_script_infos[ script_id ];
+
+   if( action == k_script_action_unlock )
+      info->availible = 1;
+
+   if( action == k_script_action_reset )
+   {
+      info->availible = 0;
+      info->viewed_time = 0;
+   }
+
+   if( action == k_script_action_play )
+   {
+      _script.script_id = script_id;
+      if( info->jump( k_escript_event_call, NULL ) )
+         _script.script_id = k_escript_script_id_max;
+
+      info->viewed_time = 9999; /* TODO */
+   }
+
+   if( action == k_script_action_info )
+   {
+      vg_info( "%s | availible: %s, viewed_time: %lu\n", 
+               info->alias,
+               info->availible? "Yes": "No",
+               info->viewed_time );
+   }
 }
 
 static int _skaterift_script_hook( int argc, const char *argv[] )
 {
-   if( argc != 1 ) 
+   if( argc != 2 ) 
    {
-      vg_error( "Usage: script <x>\n" );
+      vg_error( "Usage: script <command> <name>\n" );
       return 0;
    }
 
    if( _cutscene.state != k_cutscene_state_none )
    {
-      vg_error( "Cannot load script while cutscene is in use.\n" );
+      vg_error( "Cannot do anything while cutscene is in use.\n" );
       return 0;
    }
 
-   for( u32 i=0; i<k_escript_script_id_max; i ++ )
+   enum script_action action = k_script_action_none;
+
+   if     ( !strcmp( argv[0], "unlock")) action = k_script_action_unlock;
+   else if( !strcmp( argv[0], "play" ) ) action = k_script_action_play;
+   else if( !strcmp( argv[0], "info" ) ) action = k_script_action_info;
+   else if( !strcmp( argv[0], "reset" )) action = k_script_action_reset;
+   else                                   
    {
-      struct script_info *bind = &_script_infos[i];
-      if( vg_str_eq( argv[0], bind->alias ) ) 
+      vg_error( "'%s' is not a command\n", argv[0] );
+      return 0;
+   }
+
+   if( !strcmp( argv[1], "all" ) )
+   {
+      if( action == k_script_action_play )
       {
-         _script.script_id = i;
-         if( bind->jump( k_escript_event_call, NULL ) )
-            _script.script_id = k_escript_script_id_max;
+         vg_error( "No\n" );
+         return 0;
+      }
 
-         return 1;
+      for( u32 i=0; i<k_escript_script_id_max; i ++ )
+         _skaterift_script_hook_apply_action( i, action );
+
+      return 1;
+   }
+   else
+   {
+      for( u32 i=0; i<k_escript_script_id_max; i ++ )
+      {
+         if( vg_str_eq( argv[1], _script_infos[i].alias ) ) 
+         {
+            _skaterift_script_hook_apply_action( i, action );
+            return 1;
+         }
       }
+      
+      vg_error( "No such script '%s'\n", argv[0] );
+      return 0;
    }
+}
 
-   vg_error( "No such script '%s'\n", argv[0] );
-   return 1;
+static void _skaterift_script_hook_poll( int argc, const char *argv[] )
+{
+   const char *term = argv[argc-1];
+
+   if( argc == 1 )
+   {
+      console_suggest_score_text( "unlock", term, 0 );
+      console_suggest_score_text( "reset", term, 0 );
+      console_suggest_score_text( "play", term, 0 );
+      console_suggest_score_text( "info", term, 0 );
+   }
+   else if( argc == 2 )
+   {
+      console_suggest_score_text( "all", term, 0 );
+      for( u32 i=0; i<VG_ARRAY_LEN( _script_infos ); i ++ )
+      {
+         struct script_info *info = &_script_infos[i];
+         console_suggest_score_text( info->alias, term, 0 );
+      }
+   }
 }
 
 void _skaterift_script_update(void)
 
 void _skaterift_script_init(void)
 {
-   vg_console_reg_cmd( "script", _skaterift_script_hook, NULL );
+   vg_console_reg_cmd( "script", _skaterift_script_hook, 
+                                 _skaterift_script_hook_poll );
 }