Readded Ruby bindings, since contributor contacted me with permission to
authorRyan C. Gordon <icculus@icculus.org>
Mon, 21 Jul 2003 03:46:50 +0000
changeset 584 c0de7f19aa10
parent 583 b7fb257c8f94
child 585 760fe85ea2a0
Readded Ruby bindings, since contributor contacted me with permission to switch to zlib license.
extras/physfs_rb/installer.rb
extras/physfs_rb/physfs/extconf.rb
extras/physfs_rb/physfs/install.rb
extras/physfs_rb/physfs/make_install_test.sh
extras/physfs_rb/physfs/physfs.rb
extras/physfs_rb/physfs/physfsrwops.c
extras/physfs_rb/physfs/physfsrwops.h
extras/physfs_rb/physfs/rb_physfs.c
extras/physfs_rb/physfs/rb_physfs.h
extras/physfs_rb/physfs/rb_physfs_file.c
extras/physfs_rb/physfs/rb_physfs_file.h
extras/physfs_rb/physfs/rb_sdl_rwops.c
extras/physfs_rb/physfs/rb_sdl_rwops.h
extras/physfs_rb/physfs/test/test_physfs.rb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/installer.rb	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,103 @@
+# $Id: installer.rb,v 1.3 2003/07/21 03:46:50 icculus Exp $
+
+require 'rbconfig'
+require 'find'
+require 'ftools'
+
+include Config
+
+module Slimb
+  class Installer
+    def initialize target_dir = "", &user_skip 
+      @user_skip = user_skip or proc {|f| false}
+      
+      @version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
+      @libdir = File.join(CONFIG["libdir"], "ruby", @version)
+      @sitedir = CONFIG["sitedir"] || File.join(@libdir, "site_ruby")
+      @dest = File.join @sitedir, target_dir
+
+      File::makedirs @dest
+      File::chmod 0755, @dest, true
+    end
+
+    def skip? file
+      @user_skip[file] or
+	file[0] == ?. or file[-1] == ?~ or file[-1] == ?#
+    end
+    
+    def install_dir dir 
+      File::makedirs(File.join(@dest, dir))
+      File::chmod(0755, File.join(@dest, dir), true)
+      Dir.foreach(dir) {|file|
+	next if skip? file
+	
+	if File.ftype(File.join(dir, file)) == "directory"
+	  install_dir File.join(dir, file)
+	else
+	  install_file File.join(dir, file)
+	end
+      }
+    end
+
+    def install_file file
+      if file =~ /\.so$/
+	install_so file
+      else
+	File::install file, File.join(@dest, file), 0644, true
+      end
+    end
+
+    def install_so file
+      File::install file, File.join(CONFIG["sitearchdir"], file), 0644, true
+    end
+
+    def uninstall_so file
+      file = File.join(CONFIG["sitearchdir"], file)
+      File::safe_unlink file
+    end
+
+    def install something
+      case something
+      when Array
+	something.each {|x|
+	  install x if x.is_a? String
+	}
+      when String
+	if File.ftype(something) == "directory"
+	  install_dir something
+	else
+	  install_file something
+	end
+      end
+    end
+
+    def uninstall what = "*"
+      case what
+      when Array
+	files = what.map {|x| File.join(@dest, x)}
+      when String
+	files = Dir[File.join(@dest, what)]
+      end
+      
+      files.each {|x|
+	# FIXME: recursive uninstall is a must
+	next if FileTest.directory? x
+	File::safe_unlink x
+      }
+    end
+
+    def run files, argv
+      if !argv.grep(/--uninstall/).empty?
+	uninstall files
+      else
+	install files
+      end	
+    end
+  end
+end
+
+# self-installation 
+if $0 == __FILE__
+  $stderr.puts "Installing slimb installer..."
+  Slimb::Installer.new("slimb").install File.basename(__FILE__)
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/extconf.rb	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,9 @@
+require 'mkmf'
+
+$CFLAGS += `sdl-config --cflags`.chomp
+$LDFLAGS += `sdl-config --libs`.chomp
+
+have_library "physfs", "PHYSFS_init"
+have_library "SDL", "SDL_AllocRW"
+
+create_makefile "physfs_so"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/install.rb	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,7 @@
+#!/usr/local/bin/ruby
+
+if __FILE__ == $0
+  require 'slimb/installer'
+  files = ["physfs.rb", "physfs_so.so"]
+  installer = Slimb::Installer.new.run files, ARGV
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/make_install_test.sh	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,9 @@
+#!/bin/sh
+ruby extconf.rb
+make
+cd ..
+ruby installer.rb
+cd physfs
+ruby install.rb
+cd test
+ruby test_physfs.rb
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/physfs.rb	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,121 @@
+#
+# PhysicsFS - ruby interface
+#
+# Author: Ed Sinjiashvili (slimb@vlinkmail.com)
+# License: LGPL
+#
+
+require 'physfs_so'
+
+module PhysicsFS
+
+  class Version
+    def initialize major, minor, patch
+      @major = major
+      @minor = minor
+      @patch = patch
+    end
+
+    attr_reader :major, :minor, :patch
+
+    def to_s
+      "#@major.#@minor.#@patch"
+    end
+  end
+
+  class ArchiveInfo
+    def initialize ext, desc, author, url
+      @extension = ext
+      @description = desc
+      @author = author
+      @url = url
+    end
+
+    attr_reader :extension, :description
+    attr_reader :author, :url
+
+    def to_s
+      " * #@extension: #@description\n    Written by #@author.\n    #@url\n"
+    end
+  end
+
+  #
+  # convenience methods
+  #
+  class << self  
+
+    def init argv0 = $0
+      init_internal argv0
+    end
+
+    def append_search_path str
+      add_to_search_path str, 1
+      self
+    end
+
+    def prepend_search_path str
+      add_to_search_path str, 0
+      self
+    end
+
+    alias_method :<<,      :append_search_path
+    alias_method :push,    :append_search_path
+    alias_method :unshift, :prepend_search_path
+
+    def ls path = ""
+      enumerate path
+    end
+  end
+
+  #
+  # File - PhysicsFS abstract file - can be drawn from various sources
+  # 
+  class File
+    def write_str str
+      write str, 1, str.length
+    end
+    
+    def cat
+      prev_pos = tell
+      seek 0
+      r = read length, 1
+      seek prev_pos
+      r
+    end
+    
+    alias_method :size, :length
+  end
+
+  #
+  # RWops - general stdio like operations on file-like creatures
+  #
+  class RWops
+    SEEK_SET = 0
+    SEEK_CUR = 1
+    SEEK_END = 2
+
+    # tell current position of RWopted entity
+    def tell
+      seek 0, SEEK_CUR
+    end
+
+    # length of RWops abstracted entity
+    def length
+      cur = tell
+      r = seek 0, SEEK_END
+      seek cur, SEEK_SET
+      r
+    end
+
+    alias_method :size, :length
+
+    #
+    # create rwops from PhysicsFS file object
+    # 
+    def self.from_physfs file
+      file.to_rwops
+    end
+  end
+end
+
+# physfs.rb ends here #
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/physfsrwops.c	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,192 @@
+/*
+ * This code provides a glue layer between PhysicsFS and Simple Directmedia
+ *  Layer's (SDL) RWops i/o abstraction.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ *  correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ *  verbatim into a closed-source project, exploiting it commercially, and
+ *  removing any trace of my name from the source (although I hope you won't
+ *  do that). I welcome enhancements and corrections to this file, but I do
+ *  not require you to send me patches if you make changes.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the GNU Lesser
+ *  General Public License: http://www.gnu.org/licenses/lgpl.txt
+ *
+ * SDL falls under the LGPL, too. You can get SDL at http://www.libsdl.org/
+ *
+ *  This file was written by Ryan C. Gordon. (icculus@clutteredmind.org).
+ */
+
+#include <stdio.h>  /* used for SEEK_SET, SEEK_CUR, SEEK_END ... */
+#include "physfsrwops.h"
+
+static int physfsrwops_seek(SDL_RWops *rw, int offset, int whence)
+{
+    PHYSFS_file *handle = (PHYSFS_file *) rw->hidden.unknown.data1;
+    int pos = 0;
+
+    if (whence == SEEK_SET)
+    {
+        pos = offset;
+    } /* if */
+
+    else if (whence == SEEK_CUR)
+    {
+        PHYSFS_sint64 current = PHYSFS_tell(handle);
+        if (current == -1)
+        {
+            SDL_SetError("Can't find position in file: %s",
+                          PHYSFS_getLastError());
+            return(-1);
+        } /* if */
+
+        pos = (int) current;
+        if ( ((PHYSFS_sint64) pos) != current )
+        {
+            SDL_SetError("Can't fit current file position in an int!");
+            return(-1);
+        } /* if */
+
+        if (offset == 0)  /* this is a "tell" call. We're done. */
+            return(pos);
+
+        pos += offset;
+    } /* else if */
+
+    else if (whence == SEEK_END)
+    {
+        PHYSFS_sint64 len = PHYSFS_fileLength(handle);
+        if (len == -1)
+        {
+            SDL_SetError("Can't find end of file: %s", PHYSFS_getLastError());
+            return(-1);
+        } /* if */
+
+        pos = (int) len;
+        if ( ((PHYSFS_sint64) pos) != len )
+        {
+            SDL_SetError("Can't fit end-of-file position in an int!");
+            return(-1);
+        } /* if */
+
+        pos += offset;
+    } /* else if */
+
+    else
+    {
+        SDL_SetError("Invalid 'whence' parameter.");
+        return(-1);
+    } /* else */
+
+    if ( pos < 0 )
+    {
+        SDL_SetError("Attempt to seek past start of file.");
+        return(-1);
+    } /* if */
+    
+    if (!PHYSFS_seek(handle, (PHYSFS_uint64) pos))
+    {
+        SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+        return(-1);
+    } /* if */
+
+    return(pos);
+} /* physfsrwops_seek */
+
+
+static int physfsrwops_read(SDL_RWops *rw, void *ptr, int size, int maxnum)
+{
+    PHYSFS_file *handle = (PHYSFS_file *) rw->hidden.unknown.data1;
+    PHYSFS_sint64 rc = PHYSFS_read(handle, ptr, size, maxnum);
+    if (rc != maxnum)
+    {
+        if (!PHYSFS_eof(handle)) /* not EOF? Must be an error. */
+            SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+    } /* if */
+
+    return((int) rc);
+} /* physfsrwops_read */
+
+
+static int physfsrwops_write(SDL_RWops *rw, const void *ptr, int size, int num)
+{
+    PHYSFS_file *handle = (PHYSFS_file *) rw->hidden.unknown.data1;
+    PHYSFS_sint64 rc = PHYSFS_write(handle, ptr, size, num);
+    if (rc != num)
+        SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+
+    return((int) rc);
+} /* physfsrwops_write */
+
+
+static int physfsrwops_close(SDL_RWops *rw)
+{
+    PHYSFS_file *handle = (PHYSFS_file *) rw->hidden.unknown.data1;
+    if (!PHYSFS_close(handle))
+    {
+        SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+        return(-1);
+    } /* if */
+
+    SDL_FreeRW(rw);
+    return(0);
+} /* physfsrwops_close */
+
+
+static SDL_RWops *create_rwops(PHYSFS_file *handle)
+{
+    SDL_RWops *retval = NULL;
+
+    if (handle == NULL)
+        SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+    else
+    {
+        retval = SDL_AllocRW();
+        if (retval != NULL)
+        {
+            retval->seek  = physfsrwops_seek;
+            retval->read  = physfsrwops_read;
+            retval->write = physfsrwops_write;
+            retval->close = physfsrwops_close;
+            retval->hidden.unknown.data1 = handle;
+        } /* if */
+    } /* else */
+
+    return(retval);
+} /* create_rwops */
+
+
+SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_file *handle)
+{
+    SDL_RWops *retval = NULL;
+    if (handle == NULL)
+        SDL_SetError("NULL pointer passed to PHYSFSRWOPS_makeRWops().");
+    else
+        retval = create_rwops(handle);
+
+    return(retval);
+} /* PHYSFSRWOPS_makeRWops */
+
+
+SDL_RWops *PHYSFSRWOPS_openRead(const char *fname)
+{
+    return(create_rwops(PHYSFS_openRead(fname)));
+} /* PHYSFSRWOPS_openRead */
+
+
+SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname)
+{
+    return(create_rwops(PHYSFS_openWrite(fname)));
+} /* PHYSFSRWOPS_openWrite */
+
+
+SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname)
+{
+    return(create_rwops(PHYSFS_openAppend(fname)));
+} /* PHYSFSRWOPS_openAppend */
+
+
+/* end of physfsrwops.c ... */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/physfsrwops.h	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,87 @@
+/*
+ * This code provides a glue layer between PhysicsFS and Simple Directmedia
+ *  Layer's (SDL) RWops i/o abstraction.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ *  correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ *  verbatim into a closed-source project, exploiting it commercially, and
+ *  removing any trace of my name from the source (although I hope you won't
+ *  do that). I welcome enhancements and corrections to this file, but I do
+ *  not require you to send me patches if you make changes.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the GNU Lesser
+ *  General Public License: http://www.gnu.org/licenses/lgpl.txt
+ *
+ * SDL falls under the LGPL, too. You can get SDL at http://www.libsdl.org/
+ *
+ *  This file was written by Ryan C. Gordon. (icculus@clutteredmind.org).
+ */
+
+#ifndef _INCLUDE_PHYSFSRWOPS_H_
+#define _INCLUDE_PHYSFSRWOPS_H_
+
+#include "physfs.h"
+#include "SDL.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Open a platform-independent filename for reading, and make it accessible
+ *  via an SDL_RWops structure. The file will be closed in PhysicsFS when the
+ *  RWops is closed. PhysicsFS should be configured to your liking before
+ *  opening files through this method.
+ *
+ *   @param filename File to open in platform-independent notation.
+ *  @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ *           of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_openRead(const char *fname);
+
+/**
+ * Open a platform-independent filename for writing, and make it accessible
+ *  via an SDL_RWops structure. The file will be closed in PhysicsFS when the
+ *  RWops is closed. PhysicsFS should be configured to your liking before
+ *  opening files through this method.
+ *
+ *   @param filename File to open in platform-independent notation.
+ *  @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ *           of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname);
+
+/**
+ * Open a platform-independent filename for appending, and make it accessible
+ *  via an SDL_RWops structure. The file will be closed in PhysicsFS when the
+ *  RWops is closed. PhysicsFS should be configured to your liking before
+ *  opening files through this method.
+ *
+ *   @param filename File to open in platform-independent notation.
+ *  @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ *           of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname);
+
+/**
+ * Make a SDL_RWops from an existing PhysicsFS file handle. You should
+ *  dispose of any references to the handle after successful creation of
+ *  the RWops. The actual PhysicsFS handle will be destroyed when the
+ *  RWops is closed.
+ *
+ *   @param handle a valid PhysicsFS file handle.
+ *  @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ *           of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_file *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* include-once blocker */
+
+/* end of physfsrwops.h ... */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/rb_physfs.c	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,462 @@
+/*
+ * PhysicsFS - ruby interface
+ * 
+ * Author::  Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License:: LGPL
+ */
+
+#include "physfs.h"
+#include "ruby.h"
+
+#include "rb_physfs.h" 
+#include "rb_physfs_file.h"
+
+VALUE modulePhysfs;
+
+/*
+ * PhysicsFS::init str
+ *
+ * initialize PhysicsFS
+ */
+VALUE physfs_init (VALUE self, VALUE str)
+{
+    int result = PHYSFS_init (STR2CSTR(str));
+
+    if (result)
+	return Qtrue;
+
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::deinit
+ */
+VALUE physfs_deinit (VALUE self)
+{
+    if (PHYSFS_deinit ())
+	return Qtrue;
+
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::version
+ * 
+ * return PhysicsFS::Version object
+ */
+VALUE physfs_version (VALUE self)
+{
+    char evalStr[200];
+    PHYSFS_Version ver;
+
+    PHYSFS_getLinkedVersion (&ver);
+
+    sprintf (evalStr, "PhysicsFS::Version.new %d, %d, %d", 
+	     ver.major, ver.minor, ver.patch);
+    return rb_eval_string (evalStr);
+}
+
+/*
+ * PhysicsFS::supported_archives
+ *
+ * return Array of PhysicsFS::ArchiveInfo objects
+ */
+VALUE physfs_supported_archives (VALUE self)
+{
+    const PHYSFS_ArchiveInfo **info = PHYSFS_supportedArchiveTypes();
+    VALUE klass = rb_const_get (modulePhysfs, rb_intern ("ArchiveInfo"));
+    VALUE ary = rb_ary_new ();
+    VALUE params[4];
+
+    while ( *info != 0 ) 
+    {
+        params[0] = rb_str_new2 ((*info)->extension);
+        params[1] = rb_str_new2 ((*info)->description);
+        params[2] = rb_str_new2 ((*info)->author);
+        params[3] = rb_str_new2 ((*info)->url);
+
+        rb_ary_push (ary, rb_class_new_instance (4, params, klass));
+        info++;
+    }
+
+    return ary;
+}
+
+/*
+ * PhysicsFS::last_error
+ *
+ * return string representation of last PhysicsFS error
+ */
+VALUE physfs_last_error (VALUE self)
+{
+    const char *last_error = PHYSFS_getLastError ();
+
+    if (last_error == 0)
+	last_error = "";
+
+    return rb_str_new2 (last_error);
+}
+
+/*
+ * PhysicsFS::dir_separator
+ *
+ * return platform directory separator
+ */
+VALUE physfs_dir_separator (VALUE self)
+{
+    return rb_str_new2 (PHYSFS_getDirSeparator ());
+}
+
+/*
+ * PhysicsFS::permit_symlinks boolValue
+ *
+ * turn symlinks support on/off
+ */
+VALUE physfs_permit_symlinks (VALUE self, VALUE allow)
+{
+    int p = 1;
+    
+    if (allow == Qfalse || allow == Qnil)
+        p = 0;
+
+    PHYSFS_permitSymbolicLinks (p);
+    return Qtrue;
+}
+
+/*
+ * PhysicsFS::cdrom_dirs
+ *
+ * return Array of strings containing available CDs 
+ */
+VALUE physfs_cdrom_dirs (VALUE self)
+{
+    char **cds = PHYSFS_getCdRomDirs();
+    char **i;
+    VALUE ary = rb_ary_new ();
+
+    for (i = cds; *i != 0; i++)
+        rb_ary_push (ary, rb_str_new2 (*i));
+
+    PHYSFS_freeList (cds);
+    return ary;
+}
+
+/*
+ * PhysicsFS::base_dir
+ *
+ * return base directory
+ */
+VALUE physfs_base_dir (VALUE self)
+{
+    const char *base_dir = PHYSFS_getBaseDir ();
+    if (base_dir == 0)
+        base_dir = "";
+
+    return rb_str_new2 (base_dir);
+}
+
+/*
+ * PhysicsFS::user_dir
+ *
+ * return user directory
+ */
+VALUE physfs_user_dir (VALUE self)
+{
+    const char *user_dir = PHYSFS_getBaseDir ();
+    if (user_dir == 0)
+        user_dir = "";
+
+    return rb_str_new2 (user_dir);
+}
+   
+/*
+ * PhysicsFS::write_dir 
+ *
+ * return write directory
+ */
+VALUE physfs_write_dir (VALUE self)
+{
+    const char *write_dir = PHYSFS_getWriteDir ();
+    if (write_dir == 0)
+        return Qnil;
+
+    return rb_str_new2 (write_dir);
+}
+
+/*
+ * PhysicsFS::write_dir= str
+ *
+ * set write directory to *str*
+ */
+VALUE physfs_set_write_dir (VALUE self, VALUE str)
+{
+    int result = PHYSFS_setWriteDir (STR2CSTR(str));
+
+    if (result)
+        return Qtrue;
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::add_to_search_path str, append
+ *
+ * if append > 0 - append str to search path, otherwise prepend it
+ */
+VALUE physfs_add_search_path (VALUE self, VALUE str, VALUE append)
+{
+    int result = PHYSFS_addToSearchPath (STR2CSTR(str), FIX2INT(append));
+    if (result)
+        return Qtrue;
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::remove_from_search_path str
+ *
+ * removes str from search path
+ */
+VALUE physfs_remove_search_path (VALUE self, VALUE str)
+{
+    int result = PHYSFS_removeFromSearchPath (STR2CSTR(str));
+    if (result)
+        return Qtrue;
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::search_path
+ *
+ * return current search_path - as array of strings
+ */
+VALUE physfs_search_path (VALUE self)
+{
+    char **path = PHYSFS_getSearchPath ();
+    char **i;
+    VALUE ary = rb_ary_new ();
+
+    for (i = path ; *i != 0; i++)
+        rb_ary_push (ary, rb_str_new2 (*i));
+
+    PHYSFS_freeList (path);
+    return ary;
+}
+
+// 
+VALUE physfs_setSaneConfig(VALUE self, VALUE org, VALUE app, VALUE ext,
+                           VALUE includeCdroms, VALUE archivesFirst)
+{
+    int res = PHYSFS_setSaneConfig (STR2CSTR(org), STR2CSTR(app), STR2CSTR(ext),
+                                   RTEST(includeCdroms), RTEST(archivesFirst));
+    if (res)
+        return Qtrue;
+
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::mkdir newdir
+ *
+ * create new directory 
+ */ 
+VALUE physfs_mkdir (VALUE self, VALUE newdir)
+{
+    int result = PHYSFS_mkdir (STR2CSTR(newdir));
+    if (result)
+        return Qtrue;
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::delete name
+ *
+ * delete file with name
+ */
+VALUE physfs_delete (VALUE self, VALUE name)
+{
+    int result = PHYSFS_delete (STR2CSTR(name));
+    if (result)
+        return Qtrue;
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::real_dir name
+ *
+ * return real directory (in search path) of a name
+ */
+VALUE physfs_real_dir (VALUE self, VALUE name)
+{
+    const char *path = PHYSFS_getRealDir (STR2CSTR(name));
+    if (path == 0)
+        return Qnil;
+
+    return rb_str_new2 (path);
+}
+
+/*
+ * PhysicsFS::enumerate dir
+ *
+ * list a dir from a search path
+ */
+VALUE physfs_enumerate (VALUE self, VALUE dir)
+{
+    char **files = PHYSFS_enumerateFiles (STR2CSTR(dir));
+    char **i;
+    VALUE ary = rb_ary_new ();
+
+    for (i = files; *i != 0; i++)
+        rb_ary_push (ary, rb_str_new2 (*i));
+
+    PHYSFS_freeList (files);
+    return ary;
+}
+
+/*
+ * PhysicsFS::exists? name
+ *
+ * does a file with name exist?
+ */
+VALUE physfs_exists (VALUE self, VALUE name)
+{
+    int result = PHYSFS_exists (STR2CSTR(name));
+    if (result)
+        return Qtrue;
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::is_directory? name
+ *
+ * return true if name is directory
+ */
+VALUE physfs_is_directory (VALUE self, VALUE name)
+{
+    int result = PHYSFS_isDirectory (STR2CSTR(name));
+    if (result)
+        return Qtrue;
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::is_symlink? name
+ *
+ * return true if name is symlink
+ */
+VALUE physfs_is_symlink (VALUE self, VALUE name)
+{
+    int result = PHYSFS_isSymbolicLink (STR2CSTR(name));
+    if (result)
+        return Qtrue;
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::last_mod_time name
+ *
+ * return last modification time of a file
+ */
+VALUE physfs_last_mod_time (VALUE self, VALUE name)
+{
+    int result = PHYSFS_getLastModTime (STR2CSTR(name));
+    
+    return INT2FIX(result);
+}
+
+/*
+ * PhysicsFS::open_read name
+ *
+ * return +PhysicsFS::File+ ready for reading
+ */
+VALUE physfs_open_read (VALUE self, VALUE name)
+{
+    PHYSFS_file *file = PHYSFS_openRead (STR2CSTR(name));
+    return physfs_file_new (file);
+}
+
+/*
+ * PhysicsFS::open_write name
+ *
+ * return PhysicsFS::File ready for writing
+ */
+VALUE physfs_open_write (VALUE self, VALUE name)
+{
+    PHYSFS_file *file = PHYSFS_openWrite (STR2CSTR(name));
+    return physfs_file_new (file);
+}
+
+/*
+ * PhysicsFS::open_append name
+ *
+ * return PhysicsFS::File ready for appending
+ */
+VALUE physfs_open_append (VALUE self, VALUE name)
+{
+    PHYSFS_file *file = PHYSFS_openAppend (STR2CSTR(name));
+    return physfs_file_new (file);
+}
+
+void Init_physfs_so (void)
+{
+    modulePhysfs = rb_define_module ("PhysicsFS");
+
+    rb_define_singleton_method (modulePhysfs, "init_internal", physfs_init, 1);
+    rb_define_singleton_method (modulePhysfs, "deinit", physfs_deinit, 0);
+    rb_define_singleton_method (modulePhysfs, "version", physfs_version, 0);
+    rb_define_singleton_method (modulePhysfs, "supported_archives",
+				physfs_supported_archives, 0);
+    rb_define_singleton_method (modulePhysfs, "last_error", 
+				physfs_last_error, 0);
+    rb_define_singleton_method (modulePhysfs, "dir_separator",
+				physfs_dir_separator, 0);
+    rb_define_singleton_method (modulePhysfs, "permit_symlinks",
+                                physfs_permit_symlinks, 1);
+    rb_define_singleton_method (modulePhysfs, "cdrom_dirs", 
+                                physfs_cdrom_dirs, 0);
+    rb_define_singleton_method (modulePhysfs, "base_dir", physfs_base_dir, 0);
+    rb_define_singleton_method (modulePhysfs, "user_dir", physfs_user_dir, 0);
+
+    rb_define_singleton_method (modulePhysfs, "write_dir", physfs_write_dir, 0);
+    rb_define_singleton_method (modulePhysfs, "write_dir=", 
+                                physfs_set_write_dir, 1);
+
+    rb_define_singleton_method (modulePhysfs, "add_to_search_path",
+                                physfs_add_search_path, 2);
+    rb_define_singleton_method (modulePhysfs, "remove_from_search_path",
+                                physfs_remove_search_path, 1);
+    rb_define_singleton_method (modulePhysfs, "search_path",
+                                physfs_search_path, 0);
+
+    rb_define_singleton_method (modulePhysfs, "set_sane_config",
+                                physfs_setSaneConfig, 5);
+
+    rb_define_singleton_method (modulePhysfs, "mkdir", physfs_mkdir, 1);
+    rb_define_singleton_method (modulePhysfs, "delete", physfs_delete, 1);
+    rb_define_singleton_method (modulePhysfs, "real_dir",
+                                physfs_real_dir, 1);
+    rb_define_singleton_method (modulePhysfs, "enumerate", physfs_enumerate, 1);
+    rb_define_singleton_method (modulePhysfs, "exists?", physfs_exists, 1);
+    rb_define_singleton_method (modulePhysfs, "is_directory?", 
+                                physfs_is_directory, 1);
+    rb_define_singleton_method (modulePhysfs, "is_symlink?", 
+                                physfs_is_symlink, 1);
+    rb_define_singleton_method (modulePhysfs, "last_mod_time",
+                                physfs_last_mod_time, 1);
+
+    rb_define_singleton_method (modulePhysfs, "open_read", 
+                                physfs_open_read, 1);
+    rb_define_singleton_method (modulePhysfs, "open_write", 
+                                physfs_open_write, 1);
+    rb_define_singleton_method (modulePhysfs, "open_append", 
+                                physfs_open_append, 1);
+
+    init_physfs_file ();
+    init_sdl_rwops ();
+}
+
+/*
+// Local Variables:
+// mode: C
+// c-indentation-style: "stroustrup"
+// indent-tabs-mode: nil
+// End:
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/rb_physfs.h	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,13 @@
+/*
+ * PhysicsFS - ruby interface
+ * 
+ * Author::  Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License:: LGPL
+ */
+
+#ifndef __RB__PHYSFS__H__
+#define __RB__PHYSFS__H__
+
+extern VALUE modulePhysfs;
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/rb_physfs_file.c	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,226 @@
+/*
+ * PhysicsFS File abstraction - ruby interface
+ * 
+ * Author::  Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License:: LGPL
+ */
+
+#include "physfs.h"
+#include "ruby.h"
+
+#include "rb_physfs.h"
+#include "rb_physfs_file.h"
+#include "physfsrwops.h"
+
+VALUE classPhysfsFile;
+
+/*
+ * construct new PhysicsFS::File object
+ */
+VALUE physfs_file_new (PHYSFS_file *file)
+{
+    if (file == 0)
+        return Qnil;
+
+    return Data_Wrap_Struct (classPhysfsFile, 0, 0, file);
+}
+
+/*
+ * PhysicsFS::File#close 
+ *
+ * Close the file. It's illegal to use the object after its closure.
+ */
+VALUE physfs_file_close (VALUE self)
+{
+    int result;
+    PHYSFS_file *file;
+    Data_Get_Struct (self, PHYSFS_file, file);
+
+    if (file == 0)
+	return Qfalse;
+
+    result = PHYSFS_close (file);
+    DATA_PTR(self) = 0;
+
+    if (result)
+        return Qtrue;
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::File#read obj_size, num_objects
+ *
+ * Read *objCount* objects which are *objSize* each.
+ * return String instance containing raw data or nil if failure.
+ * #length of string will reflect real number of objects read.
+ */
+VALUE physfs_file_read (VALUE self, VALUE objSize, VALUE objCount)
+{
+    int objRead;
+    void *buffer;
+    VALUE result;
+    PHYSFS_file *file;
+
+    Data_Get_Struct (self, PHYSFS_file, file);
+    if (file == 0)
+	return Qnil; //wasted file - no read possible
+
+    buffer  = malloc (FIX2UINT(objSize) * FIX2UINT(objCount));
+    if (buffer == 0)
+	return Qnil;
+
+    objRead = PHYSFS_read (file, buffer, FIX2UINT(objSize), FIX2UINT(objCount));
+    if (objRead == -1)
+    {
+        free (buffer);
+        return Qnil;
+    }
+
+    result = rb_str_new (buffer, objRead * FIX2UINT(objSize));
+    free (buffer);
+    return result;
+}
+
+/*
+ * PhysicsFS::File#write buffer, obj_size, num_objects
+ *
+ * return nil on failure or number of objects written.
+ */
+VALUE physfs_file_write (VALUE self, VALUE buf, VALUE objSize, VALUE objCount)
+{
+    int result;
+    PHYSFS_file *file;
+
+    Data_Get_Struct (self, PHYSFS_file, file);
+    if (file == 0)
+	return Qnil;
+
+    result = PHYSFS_write (file, STR2CSTR(buf), 
+                           FIX2UINT(objSize), FIX2UINT(objCount));
+    if (result == -1)
+        return Qnil;
+
+    return INT2FIX(result);
+}
+
+/*
+ * PhysicsFS::File#eof? 
+ */
+VALUE physfs_file_eof (VALUE self)
+{
+    int result;
+    PHYSFS_file *file;
+
+    Data_Get_Struct (self, PHYSFS_file, file);
+    if (file == 0)
+	return Qnil;
+
+    result = PHYSFS_eof (file);
+
+    if (result)
+        return Qtrue;
+
+    return Qfalse;
+}
+
+/*
+ * PhysicsFS::File#tell
+ *
+ * tells current position in file
+ */
+VALUE physfs_file_tell (VALUE self)
+{
+    int result;
+    PHYSFS_file *file;
+
+    Data_Get_Struct (self, PHYSFS_file, file);
+    if (file == 0)
+	return Qnil;
+
+    result = PHYSFS_tell (file);
+
+    if (result == -1)
+        return Qnil;
+
+    return INT2FIX(result);
+}    
+
+/*
+ * PhysicsFS::File#seek pos
+ *
+ * seek to pos in file
+ */
+VALUE physfs_file_seek (VALUE self, VALUE pos)
+{
+    int result;
+    PHYSFS_file *file;
+
+    Data_Get_Struct (self, PHYSFS_file, file);
+    if (file == 0)
+	return Qnil;
+
+    result = PHYSFS_seek (file, FIX2LONG(pos));
+
+    if (result)
+        return Qtrue;
+
+    return Qfalse;    
+}
+
+/*
+ * PhysicsFS::File#length 
+ */
+VALUE physfs_file_length (VALUE self)
+{
+    int result;
+    PHYSFS_file *file;
+
+    Data_Get_Struct (self, PHYSFS_file, file);
+    if (file == 0)
+	return Qnil;
+
+    result = PHYSFS_fileLength (file);
+
+    if (result == -1)
+        return Qnil;
+
+    return INT2FIX(result);
+}
+
+/*
+ * PhysicsFS::File#to_rwops
+ *
+ * File object is converted to RWops object. 
+ * File object becomes unusable after that - every operation
+ * should be done through new-born RWops object. 
+ */
+VALUE physfs_file_to_rwops (VALUE self)
+{
+    PHYSFS_file *file;
+    SDL_RWops   *rwops;
+
+    Data_Get_Struct (self, PHYSFS_file, file);
+    if (file == 0)
+	return Qnil;
+
+    rwops = PHYSFSRWOPS_makeRWops (file);
+    if (rwops == 0)
+	return Qnil;
+
+    DATA_PTR(self) = 0; // oh, gosh, we've sacrificed ourselves!
+    return sdl_rwops_new (rwops);
+}
+
+void init_physfs_file (void)
+{
+    classPhysfsFile = rb_define_class_under (modulePhysfs, "File", rb_cObject);
+
+    rb_define_method (classPhysfsFile, "close",    physfs_file_close,    0);
+    rb_define_method (classPhysfsFile, "eof?",     physfs_file_eof,      0);
+    rb_define_method (classPhysfsFile, "tell",     physfs_file_tell,     0);
+    rb_define_method (classPhysfsFile, "seek",     physfs_file_seek,     1);
+    rb_define_method (classPhysfsFile, "length",   physfs_file_length,   0);
+    rb_define_method (classPhysfsFile, "read",     physfs_file_read,     2);
+    rb_define_method (classPhysfsFile, "write",    physfs_file_write,    3);
+    rb_define_method (classPhysfsFile, "to_rwops", physfs_file_to_rwops, 0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/rb_physfs_file.h	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,24 @@
+/*
+ * PhysicsFS File abstraction - ruby interface
+ * 
+ * Author::  Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License:: LGPL
+ */
+
+#ifndef __RB__PHYSFS__FILE__H__
+#define __RB__PHYSFS__FILE__H__
+
+extern VALUE classPhysfsFile;
+
+VALUE physfs_file_new    (PHYSFS_file *file);
+VALUE physfs_file_close  (VALUE self);
+VALUE physfs_file_read   (VALUE self, VALUE objSize, VALUE objCount);
+VALUE physfs_file_write  (VALUE self, VALUE buf, VALUE objSize, VALUE objCount);
+VALUE physfs_file_eof    (VALUE self);
+VALUE physfs_file_tell   (VALUE self);
+VALUE physfs_file_seek   (VALUE self, VALUE pos);
+VALUE physfs_file_length (VALUE self);
+
+void init_physfs_file (void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/rb_sdl_rwops.c	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,162 @@
+/*
+ * SDL_RWops - ruby interface
+ *
+ * Author::	Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License::	LGPL
+ */
+
+#include "SDL_rwops.h"
+#include "ruby.h"
+
+#include "rb_physfs.h"
+#include "rb_sdl_rwops.h"
+
+VALUE classRWops;
+
+/*
+ * RWops constructor
+ */
+VALUE sdl_rwops_new (SDL_RWops *ops)
+{
+    VALUE result; 
+
+    if (ops == 0)
+	return Qnil;
+
+    result = Data_Wrap_Struct (classRWops, 0, SDL_FreeRW, ops);
+    return result;
+}
+
+/*
+ * PhysicsFS::RWops::from_file name, mode
+ *
+ * create RWops object from file
+ */
+VALUE sdl_rwops_from_file (VALUE self, VALUE name, VALUE mode)
+{
+    SDL_RWops *ops = SDL_RWFromFile(STR2CSTR(name), STR2CSTR(mode));
+    return sdl_rwops_new (ops);
+}
+
+/*
+ * PhysicsFS::RWops::from_memory string
+ *
+ * create RWops object from memory
+ */
+VALUE sdl_rwops_from_mem (VALUE self, VALUE str)
+{
+    int	  len	   = RSTRING(str)->len;
+    void *mem	   = STR2CSTR(str);
+    SDL_RWops *ops = SDL_RWFromMem(mem, len);
+
+    return sdl_rwops_new (ops);
+}
+
+/*
+ * PhysicsFS::RWops#seek offset, whence
+ *
+ * position RWops object 
+ */
+VALUE sdl_rwops_seek (VALUE self, VALUE offset, VALUE whence)
+{
+    int result;
+    SDL_RWops *ops;
+    
+    Data_Get_Struct (self, SDL_RWops, ops);
+    if (ops == 0)
+	return Qnil;
+
+    result = SDL_RWseek(ops, FIX2INT(offset), FIX2INT(whence));
+    return INT2FIX(result);
+}
+
+/*
+ * PhysicsFS::RWops#close
+ *
+ * close RWops. No use of the object is possible after that.
+ */
+VALUE sdl_rwops_close (VALUE self)
+{
+    int result;
+    SDL_RWops *ops;
+    
+    Data_Get_Struct (self, SDL_RWops, ops);
+    if (ops == 0)
+	return Qnil;
+    
+    result = SDL_RWclose (ops);
+    DATA_PTR(self) = 0;
+
+    return INT2FIX(result);
+}
+
+/*
+ * PhysicsFS::RWops#read
+ *
+ * read from RWops object objCount objSize'd entities.
+ * return string containing raw data or nil
+ */
+VALUE sdl_rwops_read (VALUE self, VALUE objSize, VALUE objCount)
+{
+    int objRead;
+    void *buffer;
+    VALUE result;
+    SDL_RWops *ops;
+
+    Data_Get_Struct (self, SDL_RWops, ops);
+    if (ops == 0)
+	return Qnil;
+
+    buffer = malloc (FIX2UINT(objSize) * FIX2UINT(objCount));
+    if (buffer == 0)
+	return Qnil;
+
+    objRead = SDL_RWread (ops, buffer, FIX2UINT(objSize), FIX2UINT(objCount));
+    if (objRead == -1)
+    {
+	free (buffer);
+	return Qnil;
+    }
+
+    result = rb_str_new (buffer, objRead * FIX2UINT(objSize));
+    free (buffer);
+    return result;
+}
+
+/*
+ * PhysicsFS::RWops#write buffer, size, n
+ *
+ * write raw string containing n objects size length each.
+ * return number of objects written or nil
+ */
+VALUE sdl_rwops_write (VALUE self, VALUE buffer, VALUE size, VALUE n)
+{
+    int result;
+    SDL_RWops *ops;
+
+    Data_Get_Struct (self, SDL_RWops, ops);
+    if (ops == 0)
+	return Qnil;
+
+    result = SDL_RWwrite (ops, STR2CSTR(buffer), FIX2INT(size), FIX2INT(n));
+	
+    if (result == -1)
+	return Qnil;
+
+    return INT2FIX(result);
+}
+
+void init_sdl_rwops (void)
+{
+    classRWops = rb_define_class_under (modulePhysfs, "RWops", rb_cObject);
+    
+    rb_define_method (classRWops, "seek",  sdl_rwops_seek,  2);
+    rb_define_method (classRWops, "read",  sdl_rwops_read,  2);
+    rb_define_method (classRWops, "write", sdl_rwops_write, 3);
+    rb_define_method (classRWops, "close", sdl_rwops_close, 0);
+    
+    rb_define_singleton_method (classRWops, "from_file", 
+				sdl_rwops_from_file, 2);
+    rb_define_singleton_method (classRWops, "from_memory", 
+				sdl_rwops_from_mem, 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/rb_sdl_rwops.h	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,16 @@
+/*
+ * SDL_RWops - ruby interface
+ *
+ * Author::	Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License::	LGPL
+ */
+
+#ifndef __RB__SDL__RWOPS__H__
+#define __RB__SDL__RWOPS__H__
+
+extern VALUE classRWops;
+
+VALUE sdl_rwops_new (SDL_RWops *ops);
+void init_sdl_rwops (void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extras/physfs_rb/physfs/test/test_physfs.rb	Mon Jul 21 03:46:50 2003 +0000
@@ -0,0 +1,358 @@
+#
+# PhysicsFS test program - mimics real physfs_test
+#
+require 'readline'
+require 'physfs'
+
+def die msg
+  puts "#{msg} - reason: #{PhysicsFS.last_error}"
+end
+
+#
+# parse line to command and args
+# 
+def parse line
+  return false if line.nil?
+  
+  if line.strip =~ /^(.*?) (?: (?:\s+(.*)) | $)/x
+    run $1, $2
+  else
+    false
+  end
+end
+
+#
+# parse command args
+# 
+def parse_args args
+  args.strip!
+
+  dquoted  = /^ " (.*?) "/x
+  squoted  = /^ ' (.*?) '/x
+  unquoted = /^([^\s\'\"]+)/
+  
+  regexps = [dquoted, squoted, unquoted]
+  
+  result = []
+  while args != ""
+    regexps.each do |r|
+      if args =~ r
+	result << $1
+	args.sub! r, ""
+	args.sub!(/\s+/, "")
+	break
+      end
+    end
+  end
+  result
+end
+
+def usage cmd, prefix = "usage: "
+  print prefix
+  args = Commands::HELP[cmd]
+  if args
+    print cmd
+    args.scan(/\w+/).each {|x|
+      print " <#{x}>"
+    }
+    puts
+  else
+    puts %|#{cmd} (no arguments)|
+  end
+end
+  
+# commands go below
+module Commands
+  HELP = {
+    "init"           => "argv0",
+    "addarchive"     => "archiveLocation append",
+    "removearchive"  => "archiveLocation",
+    "enumerate"      => "dirToEnumerate",
+    "ls"             => "dirToEnumerate",
+    "setwritedir"    => "newWriteDir",
+    "permitsymlinks" => "1or0",
+    "setsaneconfig"  => "org appName arcExt includeCdRoms archivesFirst",
+    "mkdir"	     => "dirToMk",
+    "delete"         => "dirToDelete",
+    "getrealdir"     => "fileToFind",
+    "exists"         => "fileToCheck",
+    "isdir"          => "fileToCheck",
+    "issymlink"      => "fileToCheck",
+    "cat"            => "fileToCat",
+    "filelength"     => "fileToCheck",
+    "append"         => "fileToAppend",
+    "write"          => "fileToCreateOrTrash",
+    "getlastmodtime" => "fileToExamine"
+  }
+
+  def quit_cmd
+    exit
+  end
+
+  alias q_cmd quit_cmd
+
+  def help_cmd
+    commands = ::Commands.instance_methods.grep(/_cmd$/).sort
+    puts "Commands:"
+    commands.each do |c|
+      usage c.sub("_cmd", ""), "  - "
+    end
+
+    true
+  end
+
+  def e val
+    if val
+      puts "Successful."
+    else
+      puts "Failure. reason: #{PhysicsFS.last_error}"
+    end
+    true
+  end
+
+  def init_cmd arg
+    e PhysicsFS.init(arg)
+  end
+
+  def deinit_cmd
+    e PhysicsFS.deinit
+  end
+
+  def addarchive_cmd archive, append
+    e PhysicsFS.add_to_search_path(archive, append)
+  end
+
+  def removearchive_cmd archive
+    e PhysicsFS.remove_from_search_path archive
+  end
+
+  def enumerate_cmd path
+    entries = PhysicsFS.enumerate(path)
+    entries.each {|x|
+      puts x
+    }
+    true
+  end
+
+  alias ls_cmd enumerate_cmd
+
+  def getlasterror_cmd
+    puts "Last error is [#{PhysicsFS.last_error}]"
+    true
+  end
+
+  def getdirsep_cmd
+    puts "Directory separator is [#{PhysicsFS.dir_separator}]"
+    true
+  end
+
+  def getcdromdirs_cmd
+    dirs = PhysicsFS.cdrom_dirs
+    dirs.each {|x|
+      puts x
+    }
+    puts " total [#{dirs.length}] drives."
+    true
+  end
+
+  def getsearchpath_cmd
+    spath = PhysicsFS.search_path
+    spath.each {|x|
+      puts x
+    }
+    puts "total [#{spath.length}] directories."
+    true
+  end
+
+  def getbasedir_cmd
+    dir = PhysicsFS.base_dir
+    puts dir if dir
+    true
+  end
+
+  def getuserdir_cmd
+    puts PhysicsFS.user_dir
+    true
+  end
+
+  def getwritedir_cmd
+    dir = PhysicsFS.write_dir
+    if dir
+      puts "Write directory is [#{dir}]."
+    else
+      puts "No write directory defined."
+    end
+    true
+  end
+
+  def setwritedir_cmd dir
+    e(PhysicsFS.write_dir = dir)
+  end
+
+  def permitsymlinks_cmd val
+    if val.to_i == 1
+      PhysicsFS.permit_symlinks true
+      puts "Symlinks are now permitted"
+    else
+      PhysicsFS.permit_symlinks false
+      puts "Symlinks are now forbidden"
+    end
+    true
+  end
+
+  def setsaneconfig_cmd org, appname, ext, includeCdroms, archivesFirst
+    includeCdroms = includeCdroms.to_i == 1
+    archiveFirst = archivesFirst == 1
+    e PhysicsFS.set_sane_config(org, appname, ext, includeCdroms, archivesFirst)
+  end
+
+  def mkdir_cmd dir
+    e PhysicsFS.mkdir(dir)
+  end
+
+  def delete_cmd dir
+    e PhysicsFS.delete(dir)
+  end
+
+  def getrealdir_cmd file
+    dir = PhysicsFS.real_dir file
+    if dir
+      puts "Found at [#{dir}]"
+    else
+      puts "Not found."
+    end
+    true
+  end
+
+  def exists_cmd file
+    if PhysicsFS.exists? file
+      puts "File exists"
+    else
+      puts "File does not exist"
+    end
+    true
+  end
+
+  def isdir_cmd file
+    if PhysicsFS.is_directory? file
+      puts "File is a directory"
+    else
+      puts "File is NOT a directory"
+    end
+    true
+  end
+
+  def issymlink_cmd file
+    if PhysicsFS.is_symlink? file
+      puts "File is a symlink"
+    else
+      puts "File is NOT a symlink"
+    end
+    true
+  end
+
+  def cat_cmd filename
+    file = PhysicsFS.open_read filename
+    if file.nil?
+      puts "failed to open. reason: #{PhysicsFS.last_error}"
+      return true
+    end
+
+    puts file.cat
+    true
+  end
+
+  def filelength_cmd filename
+    file = PhysicsFS.open_read filename
+    if file.nil?
+      puts "failed to open. reason: #{PhysicsFS.last_error}"
+      return true
+    end
+
+    puts file.length
+    file.close
+    true
+  end
+
+  WRITE_STR = "Rubyfied PhysicsFS works just fine.\n\n"
+  
+  def append_cmd filename
+    file = PhysicsFS.open_append filename
+    if file.nil?
+      puts "failed to open. reason: #{PhysicsFS.last_error}"
+      return true
+    end
+
+    file.write WRITE_STR, 1, WRITE_STR.length
+    file.close
+    true
+  end
+
+  def write_cmd filename
+    file = PhysicsFS.open_write filename
+    if file.nil?
+      puts "failed to open. reason: #{PhysicsFS.last_error}"
+      return true
+    end
+
+    file.write_str WRITE_STR
+    file.close
+    true
+  end
+
+  def getlastmodtime_cmd filename
+    t = PhysicsFS.last_mod_time filename
+    if t == -1
+      puts "failed to determin. reason: #{PhysicsFS.last_error}"
+    else
+      puts "Last modified: #{Time.at(t)}"
+    end
+    true
+  end
+end
+
+include Commands
+
+def run command, args
+  if args
+    args = parse_args args
+  else
+    args = []
+  end
+
+  begin
+    cmd = method "#{command}_cmd"
+    if args.length == cmd.arity
+      return cmd.call *args
+    else
+      usage command
+      true
+    end
+  rescue NameError
+    puts 'Unknown command. Enter "help" for instructions.'
+    true
+  end
+end
+
+if __FILE__ == $0
+  
+  PhysicsFS.init($0) or die "PhysicsFS init failed"
+  
+  puts "PhysicsFS version: #{PhysicsFS.version}"
+  puts
+
+  puts "Supported archives: "
+  puts PhysicsFS.supported_archives
+  puts
+
+  puts 'Enter commands. Enter "help" for instructions.'
+
+  loop {
+    line = Readline::readline "physfs_rb> ", true
+    break unless parse line
+  }
+end
+
+
+
+