Index: libroar/vs.c
===================================================================
--- libroar/vs.c	(revision 4579)
+++ libroar/vs.c	(revision 4580)
@@ -36,8 +36,11 @@
 #include "libroar.h"
 
-#define FLAG_NONE     0x0000
-#define FLAG_STREAM   0x0001
-#define FLAG_NONBLOCK 0x0002
-#define FLAG_BUFFERED 0x0004
+#define FLAG_NONE      0x0000
+#define FLAG_STREAM    0x0001
+#define FLAG_NONBLOCK  0x0002
+#define FLAG_BUFFERED  0x0004
+#define FLAG_CLOSEFILE 0x0008
+#define FLAG_DIR_IN    0x1000
+#define FLAG_DIR_OUT   0x2000
 
 #define _initerr()  do { errno = 0; roar_err_clear(); } while(0)
@@ -57,4 +60,7 @@
  int                      mixerid;
  int                      first_primid;
+ struct roar_buffer     * readbuffer, * writebuffer;
+ struct roar_vio_calls    file_store;
+ struct roar_vio_calls  * file;
 };
 
@@ -173,4 +179,8 @@
  vss->flags |= FLAG_STREAM;
 
+ switch (dir) {
+  case ROAR_DIR_PLAY: vss->flags |= FLAG_DIR_OUT; break;
+ }
+
  return 0;
 }
@@ -200,4 +210,111 @@
 }
 
+int roar_vs_file(roar_vs_t * vss, struct roar_vio_calls * vio, int closefile, int * error) {
+ _ckvss(-1);
+
+ if ( vio == NULL || (closefile != ROAR_VS_TRUE && closefile != ROAR_VS_FALSE)) {
+  _seterr(ROAR_ERROR_INVAL);
+  return -1;
+ }
+
+ if ( vss->file != NULL ) {
+  _seterr(ROAR_ERROR_INVAL);
+  return -1;
+ }
+
+ vss->file = vio;
+ if ( closefile == ROAR_VS_TRUE )
+  vss->flags |= FLAG_CLOSEFILE;
+
+ return 0;
+}
+
+int roar_vs_file_simple(roar_vs_t * vss, char * filename, int * error) {
+ struct roar_vio_defaults def;
+ struct roar_vio_calls * file;
+ char buf[64];
+ ssize_t ret;
+ int dir = O_RDONLY;
+ int codec;
+
+ _ckvss(-1);
+
+ if ( vss->file != NULL ) {
+  _seterr(ROAR_ERROR_INVAL);
+  return -1;
+ }
+
+ if ( vss->flags & FLAG_STREAM ) {
+  switch (vss->flags & (FLAG_DIR_IN|FLAG_DIR_OUT)) {
+   case FLAG_DIR_IN:  dir = O_WRONLY; break;
+   case FLAG_DIR_OUT: dir = O_RDONLY; break;
+   case FLAG_DIR_IN|FLAG_DIR_OUT: dir = O_RDWR; break;
+   default: return -1;
+  }
+ }
+
+ file = &(vss->file_store);
+
+ // TODO: FIXME: correct error handling bellow.
+
+ if ( roar_vio_dstr_init_defaults(&def, ROAR_VIO_DEF_TYPE_NONE, dir, 0644) == -1 )
+  return -11;
+
+ if ( roar_vio_open_dstr(file, filename, &def, 1) == -1 ) {
+  return -1;
+ }
+
+ if ( !(vss->flags & FLAG_STREAM) ) {
+  ret = roar_vio_read(file, buf, sizeof(buf));
+
+  codec = roar_file_codecdetect(buf, ret);
+
+  if ( vss->info.codec == -1 ) {
+   roar_vio_close(file);
+   return -1;
+  }
+
+  memset(&(vss->info), 0, sizeof(vss->info));
+
+  vss->info.rate     = ROAR_RATE_DEFAULT;
+  vss->info.channels = ROAR_CHANNELS_DEFAULT;
+  vss->info.codec    = codec;
+  vss->info.bits     = ROAR_BITS_DEFAULT;
+
+  if ( roar_vio_lseek(file, 0, SEEK_SET) != 0 ) {
+   roar_vio_close(file);
+   return -1;
+  }
+
+  ret = roar_vs_stream(vss, &(vss->info), ROAR_DIR_PLAY, error);
+
+  if ( ret == -1 ) {
+   roar_vio_close(file);
+   return -1;
+  }
+ }
+
+ if ( roar_vs_file(vss, file, ROAR_VS_TRUE, error) == -1 ) {
+  roar_vio_close(file);
+  return -1;
+ }
+
+ return 0;
+}
+
+roar_vs_t * roar_vs_new_from_file(const char * server, const char * name, char * filename, int * error) {
+ roar_vs_t * vss = roar_vs_new(server, name, error);
+
+ if ( vss == NULL )
+  return NULL;
+
+ if ( roar_vs_file_simple(vss, filename, error) != 0 ) {
+  roar_vs_close(vss, ROAR_VS_TRUE, NULL);
+  return NULL;
+ }
+
+ return vss;
+}
+
 int roar_vs_close(roar_vs_t * vss, int killit, int * error) {
  if ( killit != ROAR_VS_TRUE && killit != ROAR_VS_FALSE ) {
@@ -207,4 +324,12 @@
 
  _ckvss(-1);
+
+ if ( vss->readbuffer != NULL )
+  roar_buffer_free(vss->readbuffer);
+ if ( vss->writebuffer != NULL )
+  roar_buffer_free(vss->writebuffer);
+
+ if ( vss->file != NULL && vss->flags & FLAG_CLOSEFILE )
+  roar_vio_close(vss->file);
 
  if ( vss->flags & FLAG_STREAM ) {
@@ -746,4 +871,212 @@
 }
 
+
+int     roar_vs_iterate       (roar_vs_t * vss, int wait, int * error) {
+ struct roar_vio_select vios[3];
+ struct roar_vio_selecttv rtv = {.sec = 0, .nsec = 1};
+ size_t len = 0;
+ ssize_t i;
+ ssize_t ret;
+ int can_read = 0, can_write = 0;
+ int can_flush_stream = 0, can_flush_file = 0;
+ int is_eof = 0;
+ void * data;
+
+ _ckvss(-1);
+
+ if ( wait != ROAR_VS_WAIT && wait != ROAR_VS_NOWAIT ) {
+  _seterr(ROAR_ERROR_INVAL);
+  return -1;
+ }
+
+ ROAR_VIO_SELECT_SETVIO(&(vios[len]), &(vss->vio), ((vss->flags & FLAG_DIR_IN  ? ROAR_VIO_SELECT_READ  : 0) |
+                                                    (vss->flags & FLAG_DIR_OUT ? ROAR_VIO_SELECT_WRITE : 0)));
+ vios[len].ud.vp = &(vss->vio);
+ len++;
+
+ ROAR_VIO_SELECT_SETVIO(&(vios[len]), roar_get_connection_vio2(vss->con), ROAR_VIO_SELECT_READ);
+ vios[len].ud.vp = vss->con;
+ len++;
+
+
+// TODO: FIXME: need to do two select()s so we can sleep more efficently and test for both directions.
+// for the moment we disable file direction and hope it will not block anyway...
+/*
+ if ( vss->file != NULL ) {
+  ROAR_VIO_SELECT_SETVIO(&(vios[len]), vss->file, ((vss->flags & FLAG_DIR_IN  ? ROAR_VIO_SELECT_WRITE : 0) |
+                                                   (vss->flags & FLAG_DIR_OUT ? ROAR_VIO_SELECT_READ  : 0)));
+  vios[len].ud.vp = vss->file;
+  len++;
+ }
+*/
+
+ ret = roar_vio_select(vios, len, (wait == ROAR_VS_NOWAIT ? &rtv : NULL), NULL);
+
+// part 2 of above hack:
+// emulate read handle.
+  vios[len].ud.vp   = vss->file;
+  vios[len].eventsa = ROAR_VIO_SELECT_WRITE|ROAR_VIO_SELECT_READ;;
+  len++;
+
+ // no error here nor EOF.
+ if ( ret == 0 )
+  return 1;
+
+ for (i = 0; i < len; i++) {
+  if ( !vios[i].eventsa )
+   continue;
+
+  if ( vios[i].ud.vp == &(vss->vio) ) {
+   if ( vios[i].eventsa & ROAR_VIO_SELECT_READ )
+    can_read++;
+
+   if ( vios[i].eventsa & ROAR_VIO_SELECT_WRITE ) {
+    can_write++;
+    can_flush_stream = 1;
+   }
+  } else if ( vios[i].ud.vp == vss->con ) {
+   roar_sync(vss->con);
+  } else if ( vios[i].ud.vp == vss->file ) {
+   if ( vios[i].eventsa & ROAR_VIO_SELECT_READ )
+    can_write++;
+
+   if ( vios[i].eventsa & ROAR_VIO_SELECT_WRITE ) {
+    can_read++;
+    can_flush_file = 1;
+   }
+  }
+ }
+
+ // TODO: FIXME: Need to correct error handling here!
+
+ if ( can_flush_stream && vss->writebuffer != NULL ) {
+  if ( roar_buffer_get_data(vss->writebuffer, &data) == -1 )
+   return -1;
+
+  if ( roar_buffer_get_len(vss->writebuffer, &len) == -1 )
+   return -1;
+
+  ret = roar_vs_write(vss, data, len, error);
+
+  if ( ret == -1 ) {
+   return -1;
+  } else if ( ret == len ) {
+   roar_buffer_free(vss->writebuffer);
+   vss->writebuffer = NULL;
+  } else {
+   if ( roar_buffer_set_offset(vss->writebuffer, ret) == -1 )
+    return -1;
+  }
+ }
+
+ if ( can_flush_file && vss->readbuffer != NULL ) {
+  if ( roar_buffer_get_data(vss->readbuffer, &data) == -1 )
+   return -1;
+
+  if ( roar_buffer_get_len(vss->readbuffer, &len) == -1 )
+   return -1;
+
+  ret = roar_vio_write(vss->file, data, len);
+
+  if ( ret == -1 ) {
+   return -1;
+  } else if ( ret == len ) {
+   roar_buffer_free(vss->readbuffer);
+   vss->readbuffer = NULL;
+  } else {
+   if ( roar_buffer_set_offset(vss->readbuffer, ret) == -1 )
+    return -1;
+  }
+ }
+
+#define _READ_SIZE 1024
+
+ if ( can_read == 2 && vss->readbuffer == NULL ) {
+  if ( roar_buffer_new_data(&(vss->readbuffer), (len = _READ_SIZE), &data) == -1 ) 
+   return -1;
+
+  ret = roar_vs_read(vss, data, len, error);
+
+  if ( ret == -1 ) {
+   roar_buffer_free(vss->readbuffer);
+   vss->readbuffer = NULL;
+   return -1;
+  } else if ( ret == 0 ) {
+   is_eof = 1;
+   roar_buffer_free(vss->readbuffer);
+   vss->readbuffer = NULL;
+  } else {
+   len = ret;
+   if ( roar_buffer_set_len(vss->readbuffer, len) == -1 )
+    return -1;
+
+   ret = roar_vio_write(vss->file, data, len);
+
+   if ( ret == -1 ) {
+    return -1;
+   } else if ( ret == len ) {
+    roar_buffer_free(vss->readbuffer);
+    vss->readbuffer = NULL;
+   } else {
+    if ( roar_buffer_set_offset(vss->readbuffer, ret) == -1 )
+     return -1;
+   }
+  }
+ }
+
+ if ( can_write == 2 && vss->writebuffer == NULL ) {
+  if ( roar_buffer_new_data(&(vss->writebuffer), (len = _READ_SIZE), &data) == -1 )
+   return -1;
+
+  ret = roar_vio_read(vss->file, data, len);
+
+  if ( ret == -1 ) {
+   roar_buffer_free(vss->writebuffer);
+   vss->readbuffer = NULL;
+   return -1;
+  } else if ( ret == 0 ) {
+   is_eof = 1;
+   roar_buffer_free(vss->writebuffer);
+   vss->readbuffer = NULL;
+  } else {
+   if ( len != ret ) {
+    len = ret;
+    if ( roar_buffer_set_len(vss->readbuffer, len) == -1 )
+     return -1;
+   }
+
+   ret = roar_vs_write(vss, data, len, error);
+
+   if ( ret == -1 ) {
+    return -1;
+   } else if ( ret == len ) {
+    roar_buffer_free(vss->writebuffer);
+    vss->writebuffer = NULL;
+   } else {
+    if ( roar_buffer_set_offset(vss->writebuffer, ret) == -1 )
+     return -1;
+   }
+  }
+ }
+
+ return is_eof ? 0 : 1;
+}
+
+int     roar_vs_run           (roar_vs_t * vss, int * error) {
+ int ret;
+
+ _ckvss(-1);
+
+ while ((ret = roar_vs_iterate(vss, ROAR_VS_WAIT, error)) > 0);
+
+ if ( ret == 0 ) {
+  // flush buffers:
+  roar_vs_iterate(vss, ROAR_VS_WAIT, error);
+ }
+
+ return ret;
+}
+
+
 struct roar_connection * roar_vs_connection_obj(roar_vs_t * vss, int * error) {
  _ckvss(NULL);
