From e0208e089f4f1b2ef43e947f7b2365cba47d4deb Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 21 Jul 2003 03:46:50 +0000 Subject: [PATCH] Readded Ruby bindings, since contributor contacted me with permission to switch to zlib license. --- extras/physfs_rb/installer.rb | 103 +++++ extras/physfs_rb/physfs/extconf.rb | 9 + extras/physfs_rb/physfs/install.rb | 7 + extras/physfs_rb/physfs/make_install_test.sh | 9 + extras/physfs_rb/physfs/physfs.rb | 121 +++++ extras/physfs_rb/physfs/physfsrwops.c | 192 ++++++++ extras/physfs_rb/physfs/physfsrwops.h | 87 ++++ extras/physfs_rb/physfs/rb_physfs.c | 462 +++++++++++++++++++ extras/physfs_rb/physfs/rb_physfs.h | 13 + extras/physfs_rb/physfs/rb_physfs_file.c | 226 +++++++++ extras/physfs_rb/physfs/rb_physfs_file.h | 24 + extras/physfs_rb/physfs/rb_sdl_rwops.c | 162 +++++++ extras/physfs_rb/physfs/rb_sdl_rwops.h | 16 + extras/physfs_rb/physfs/test/test_physfs.rb | 358 ++++++++++++++ 14 files changed, 1789 insertions(+) create mode 100644 extras/physfs_rb/installer.rb create mode 100644 extras/physfs_rb/physfs/extconf.rb create mode 100644 extras/physfs_rb/physfs/install.rb create mode 100755 extras/physfs_rb/physfs/make_install_test.sh create mode 100644 extras/physfs_rb/physfs/physfs.rb create mode 100644 extras/physfs_rb/physfs/physfsrwops.c create mode 100644 extras/physfs_rb/physfs/physfsrwops.h create mode 100644 extras/physfs_rb/physfs/rb_physfs.c create mode 100644 extras/physfs_rb/physfs/rb_physfs.h create mode 100644 extras/physfs_rb/physfs/rb_physfs_file.c create mode 100644 extras/physfs_rb/physfs/rb_physfs_file.h create mode 100644 extras/physfs_rb/physfs/rb_sdl_rwops.c create mode 100644 extras/physfs_rb/physfs/rb_sdl_rwops.h create mode 100644 extras/physfs_rb/physfs/test/test_physfs.rb diff --git a/extras/physfs_rb/installer.rb b/extras/physfs_rb/installer.rb new file mode 100644 index 00000000..a62bd288 --- /dev/null +++ b/extras/physfs_rb/installer.rb @@ -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 diff --git a/extras/physfs_rb/physfs/extconf.rb b/extras/physfs_rb/physfs/extconf.rb new file mode 100644 index 00000000..f344059c --- /dev/null +++ b/extras/physfs_rb/physfs/extconf.rb @@ -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" diff --git a/extras/physfs_rb/physfs/install.rb b/extras/physfs_rb/physfs/install.rb new file mode 100644 index 00000000..29bd4548 --- /dev/null +++ b/extras/physfs_rb/physfs/install.rb @@ -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 diff --git a/extras/physfs_rb/physfs/make_install_test.sh b/extras/physfs_rb/physfs/make_install_test.sh new file mode 100755 index 00000000..ac616c84 --- /dev/null +++ b/extras/physfs_rb/physfs/make_install_test.sh @@ -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 diff --git a/extras/physfs_rb/physfs/physfs.rb b/extras/physfs_rb/physfs/physfs.rb new file mode 100644 index 00000000..88da331b --- /dev/null +++ b/extras/physfs_rb/physfs/physfs.rb @@ -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 # diff --git a/extras/physfs_rb/physfs/physfsrwops.c b/extras/physfs_rb/physfs/physfsrwops.c new file mode 100644 index 00000000..474d6b29 --- /dev/null +++ b/extras/physfs_rb/physfs/physfsrwops.c @@ -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 /* 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 ... */ + diff --git a/extras/physfs_rb/physfs/physfsrwops.h b/extras/physfs_rb/physfs/physfsrwops.h new file mode 100644 index 00000000..91ff2eb2 --- /dev/null +++ b/extras/physfs_rb/physfs/physfsrwops.h @@ -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 ... */ + diff --git a/extras/physfs_rb/physfs/rb_physfs.c b/extras/physfs_rb/physfs/rb_physfs.c new file mode 100644 index 00000000..f05c586a --- /dev/null +++ b/extras/physfs_rb/physfs/rb_physfs.c @@ -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: +*/ diff --git a/extras/physfs_rb/physfs/rb_physfs.h b/extras/physfs_rb/physfs/rb_physfs.h new file mode 100644 index 00000000..ca820366 --- /dev/null +++ b/extras/physfs_rb/physfs/rb_physfs.h @@ -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 diff --git a/extras/physfs_rb/physfs/rb_physfs_file.c b/extras/physfs_rb/physfs/rb_physfs_file.c new file mode 100644 index 00000000..1aaa36e3 --- /dev/null +++ b/extras/physfs_rb/physfs/rb_physfs_file.c @@ -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); +} diff --git a/extras/physfs_rb/physfs/rb_physfs_file.h b/extras/physfs_rb/physfs/rb_physfs_file.h new file mode 100644 index 00000000..5cc1b218 --- /dev/null +++ b/extras/physfs_rb/physfs/rb_physfs_file.h @@ -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 diff --git a/extras/physfs_rb/physfs/rb_sdl_rwops.c b/extras/physfs_rb/physfs/rb_sdl_rwops.c new file mode 100644 index 00000000..6574906a --- /dev/null +++ b/extras/physfs_rb/physfs/rb_sdl_rwops.c @@ -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); +} diff --git a/extras/physfs_rb/physfs/rb_sdl_rwops.h b/extras/physfs_rb/physfs/rb_sdl_rwops.h new file mode 100644 index 00000000..05f51bcc --- /dev/null +++ b/extras/physfs_rb/physfs/rb_sdl_rwops.h @@ -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 diff --git a/extras/physfs_rb/physfs/test/test_physfs.rb b/extras/physfs_rb/physfs/test/test_physfs.rb new file mode 100644 index 00000000..3f194c19 --- /dev/null +++ b/extras/physfs_rb/physfs/test/test_physfs.rb @@ -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 + + + +