Browse Source

Implemented BRL.IO abstraction.

Brucey 5 năm trước cách đây
mục cha
commit
47e0e66fd5

+ 4 - 0
blitz.mod/blitz_app.c

@@ -11,6 +11,8 @@ BBArray*	bbAppArgs=BBNULLARRAY;
 
 
 void **bbGCStackTop;
 void **bbGCStackTop;
 
 
+char * bbArgv0 = NULL;
+
 void bbEnd(){
 void bbEnd(){
 	exit(0);
 	exit(0);
 }
 }
@@ -349,6 +351,8 @@ void bbStartup( int argc,char *argv[],void *dummy1,void *dummy2 ){
 	int i,k;
 	int i,k;
 	BBString **p;
 	BBString **p;
 	
 	
+	bbArgv0 = argv[0];
+	
 	//Start up GC and create bbAppFile, bbAppDir and bbLaunchDir
 	//Start up GC and create bbAppFile, bbAppDir and bbLaunchDir
 	
 	
 #if _WIN32
 #if _WIN32

+ 2 - 0
blitz.mod/blitz_app.h

@@ -22,6 +22,8 @@ extern BBArray*	bbAppArgs;
 
 
 extern void**		bbGCStackTop;
 extern void**		bbGCStackTop;
 
 
+extern char * bbArgv0;
+
 void		bbEnd();
 void		bbEnd();
 void		bbOnEnd( void(*f)() );
 void		bbOnEnd( void(*f)() );
 
 

+ 121 - 51
filesystem.mod/filesystem.bmx

@@ -38,6 +38,9 @@ Const FILETIME_MODIFIED:Int=0,FILETIME_CREATED:Int=1
 Private
 Private
 
 
 Function _RootPath$( path$ )
 Function _RootPath$( path$ )
+	If MaxIO.ioInitialized Then
+		Return "/"
+	End If
 ?Win32
 ?Win32
 	If path.StartsWith( "//" )
 	If path.StartsWith( "//" )
 		Return path[ ..path.Find( "/",2 )+1 ]
 		Return path[ ..path.Find( "/",2 )+1 ]
@@ -68,6 +71,7 @@ Public
 
 
 Function FixPath( path$ Var,dirPath:Int=False )
 Function FixPath( path$ Var,dirPath:Int=False )
 	path=path.Replace("\","/")
 	path=path.Replace("\","/")
+	If Not MaxIO.ioInitialized Then
 ?Win32
 ?Win32
 	If path.StartsWith( "//" )
 	If path.StartsWith( "//" )
 		If path.Find( "/",2 )=-1 path:+"/"
 		If path.Find( "/",2 )=-1 path:+"/"
@@ -79,6 +83,7 @@ Function FixPath( path$ Var,dirPath:Int=False )
 		EndIf
 		EndIf
 	EndIf
 	EndIf
 ?
 ?
+	End If
 	If dirPath And path.EndsWith( "/" ) 
 	If dirPath And path.EndsWith( "/" ) 
 		If Not _IsRootPath( path ) path=path[..path.length-1]
 		If Not _IsRootPath( path ) path=path[..path.length-1]
 	EndIf
 	EndIf
@@ -152,6 +157,9 @@ bbdoc: Get Current Directory
 returns: The current directory
 returns: The current directory
 End Rem
 End Rem
 Function CurrentDir$()
 Function CurrentDir$()
+	If MaxIO.ioInitialized Then
+		Return "/"
+	End If
 	Local path$=getcwd_()
 	Local path$=getcwd_()
 	FixPath path
 	FixPath path
 	Return path
 	Return path
@@ -200,12 +208,21 @@ returns: 0 if file at @path doesn't exist, FILETYPE_FILE (1) if the file is a pl
 End Rem
 End Rem
 Function FileType:Int( path$ )
 Function FileType:Int( path$ )
 	FixPath path
 	FixPath path
-	Local Mode:Int,size:Long,mtime:Int,ctime:Int
-	If stat_( path,Mode,size,mtime,ctime ) Return 0
-	Select Mode & S_IFMT_
-	Case S_IFREG_ Return FILETYPE_FILE
-	Case S_IFDIR_ Return FILETYPE_DIR
-	End Select
+	If MaxIO.ioInitialized Then
+		Local stat:SMaxIO_Stat
+		If Not MaxIO.Stat(path, stat) Return 0
+		Select stat._filetype
+			Case EMaxIOFileType.REGULAR Return FILETYPE_FILE
+			Case EMaxIOFileType.DIRECTORY Return FILETYPE_DIR
+		End Select
+	Else
+		Local Mode:Int,size:Long,mtime:Int,ctime:Int
+		If stat_( path,Mode,size,mtime,ctime ) Return 0
+		Select Mode & S_IFMT_
+		Case S_IFREG_ Return FILETYPE_FILE
+		Case S_IFDIR_ Return FILETYPE_DIR
+		End Select
+	End If
 	Return FILETYPE_NONE
 	Return FILETYPE_NONE
 End Function
 End Function
 
 
@@ -213,15 +230,25 @@ Rem
 bbdoc: Get file time
 bbdoc: Get file time
 returns: The time the file at @path was last modified 
 returns: The time the file at @path was last modified 
 End Rem
 End Rem
-Function FileTime:Int( path$, timetype:Int=FILETIME_MODIFIED )
+Function FileTime:Long( path$, timetype:Int=FILETIME_MODIFIED )
 	FixPath path
 	FixPath path
-	Local Mode:Int,size:Long,mtime:Int,ctime:Int
-	If stat_( path,Mode,size,mtime,ctime ) Return 0
-	If(timetype = FILETIME_CREATED)
-		Return ctime
+	If MaxIO.ioInitialized Then
+		Local stat:SMaxIO_Stat
+		If Not MaxIO.Stat(path, stat) Return 0
+		If timetype = FILETIME_CREATED Then
+			Return stat._createtime
+		Else
+			Return stat._modtime
+		End If
 	Else
 	Else
-		Return mtime
-	EndIf
+		Local Mode:Int,size:Long,mtime:Int,ctime:Int
+		If stat_( path,Mode,size,mtime,ctime ) Return 0
+		If(timetype = FILETIME_CREATED)
+			Return ctime
+		Else
+			Return mtime
+		EndIf
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
@@ -230,9 +257,15 @@ returns: Size, in bytes, of the file at @path, or -1 if the file does not exist
 end rem
 end rem
 Function FileSize:Long( path$ )
 Function FileSize:Long( path$ )
 	FixPath path
 	FixPath path
-	Local Mode:Int,size:Long,mtime:Int,ctime:Int
-	If stat_( path,Mode,size,mtime,ctime ) Return -1
-	Return size
+	If MaxIO.ioInitialized Then
+		Local stat:SMaxIO_Stat
+		If Not MaxIO.Stat(path, stat) Return -1
+		Return stat._filesize
+	Else
+		Local Mode:Int,size:Long,mtime:Int,ctime:Int
+		If stat_( path,Mode,size,mtime,ctime ) Return -1
+		Return size
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
@@ -241,9 +274,11 @@ returns: file mode flags
 end rem
 end rem
 Function FileMode:Int( path$ )
 Function FileMode:Int( path$ )
 	FixPath path
 	FixPath path
-	Local Mode:Int,size:Long,mtime:Int,ctime:Int
-	If stat_( path,Mode,size,mtime,ctime ) Return -1
-	Return Mode & 511
+	If Not MaxIO.ioInitialized Then
+		Local Mode:Int,size:Long,mtime:Int,ctime:Int
+		If stat_( path,Mode,size,mtime,ctime ) Return -1
+		Return Mode & 511
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
@@ -251,7 +286,9 @@ bbdoc: Set file mode
 end rem
 end rem
 Function SetFileMode( path$,Mode:Int )
 Function SetFileMode( path$,Mode:Int )
 	FixPath path
 	FixPath path
-	chmod_ path,Mode
+	If Not MaxIO.ioInitialized Then
+		chmod_ path,Mode
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
@@ -260,9 +297,15 @@ returns: True if successful
 End Rem
 End Rem
 Function CreateFile:Int( path$ )
 Function CreateFile:Int( path$ )
 	FixPath path
 	FixPath path
-	remove_ path
-	Local t:Byte Ptr=fopen_( path,"wb" )
-	If t fclose_ t
+	If MaxIO.ioInitialized Then
+		MaxIO.DeletePath(path)
+		Local t:Byte Ptr = MaxIO.OpenWrite(path)
+		If t MaxIO.Close(t)
+	Else
+		remove_ path
+		Local t:Byte Ptr=fopen_( path,"wb" )
+		If t fclose_ t
+	End If
 	If FileType( path )=FILETYPE_FILE Return True
 	If FileType( path )=FILETYPE_FILE Return True
 End Function
 End Function
 
 
@@ -274,27 +317,31 @@ If @recurse is true, any required subdirectories are also created.
 End Rem
 End Rem
 Function CreateDir:Int( path$,recurse:Int=False )
 Function CreateDir:Int( path$,recurse:Int=False )
 	FixPath path,True
 	FixPath path,True
-	If Not recurse
-		mkdir_ path,1023
-		Return FileType(path)=FILETYPE_DIR
-	EndIf
-	Local t$
-	path=RealPath(path)+"/"
-	While path
-		Local i:Int=path.find("/")+1
-		t:+path[..i]
-		path=path[i..]
-		Select FileType(t)
-		Case FILETYPE_DIR
-		Case FILETYPE_NONE
-			Local s$=StripSlash(t)
-			mkdir_ StripSlash(s),1023
-			If FileType(s)<>FILETYPE_DIR Return False
-		Default
-			Return False
-		End Select
-	Wend
-	Return True
+	If MaxIO.ioInitialized Then
+		Return MaxIO.MkDir(path)
+	Else
+		If Not recurse
+			mkdir_ path,1023
+			Return FileType(path)=FILETYPE_DIR
+		EndIf
+		Local t$
+		path=RealPath(path)+"/"
+		While path
+			Local i:Int=path.find("/")+1
+			t:+path[..i]
+			path=path[i..]
+			Select FileType(t)
+			Case FILETYPE_DIR
+			Case FILETYPE_NONE
+				Local s$=StripSlash(t)
+				mkdir_ StripSlash(s),1023
+				If FileType(s)<>FILETYPE_DIR Return False
+			Default
+				Return False
+			End Select
+		Wend
+		Return True
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
@@ -303,7 +350,11 @@ returns: True if successful
 End Rem
 End Rem
 Function DeleteFile:Int( path$ )
 Function DeleteFile:Int( path$ )
 	FixPath path
 	FixPath path
-	remove_ path
+	If MaxIO.ioInitialized Then
+		MaxIO.DeletePath(path)
+	Else
+		remove_ path
+	End If
 	Return FileType(path)=FILETYPE_NONE
 	Return FileType(path)=FILETYPE_NONE
 End Function
 End Function
 
 
@@ -312,6 +363,9 @@ bbdoc: Renames a file
 returns: True if successful
 returns: True if successful
 End Rem
 End Rem
 Function RenameFile:Int( oldpath$,newpath$ )
 Function RenameFile:Int( oldpath$,newpath$ )
+	If MaxIO.ioInitialized Then
+		Return False
+	End If
 	FixPath oldpath
 	FixPath oldpath
 	FixPath newpath
 	FixPath newpath
 	Return rename_( oldpath,newpath)=0
 	Return rename_( oldpath,newpath)=0
@@ -399,17 +453,25 @@ bbdoc: Change current directory
 returns: True if successful
 returns: True if successful
 End Rem
 End Rem
 Function ChangeDir:Int( path$ )
 Function ChangeDir:Int( path$ )
-	FixPath path,True
-	If chdir_( path )=0 Return True
+	If MaxIO.ioInitialized Then
+		Return False
+	Else
+		FixPath path,True
+		If chdir_( path )=0 Return True
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
 bbdoc: Open a directory
 bbdoc: Open a directory
-returns: A directory handle, or 0 if the directory does not exist
+returns: A directory handle, or Null if the directory does not exist
 End Rem
 End Rem
 Function ReadDir:Byte Ptr( path$ )
 Function ReadDir:Byte Ptr( path$ )
 	FixPath path,True
 	FixPath path,True
-	Return opendir_( path )
+	If MaxIO.ioInitialized Then
+		Return bmx_blitzio_readdir(path)
+	Else
+		Return opendir_( path )
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
@@ -417,14 +479,22 @@ bbdoc: Return next file in a directory
 returns: File name of next file in directory opened using #ReadDir, or an empty string if there are no more files to read.
 returns: File name of next file in directory opened using #ReadDir, or an empty string if there are no more files to read.
 End Rem
 End Rem
 Function NextFile$( dir:Byte Ptr )
 Function NextFile$( dir:Byte Ptr )
-	Return readdir_( dir )
+	If MaxIO.ioInitialized Then
+		Return bmx_blitzio_nextFile(dir)
+	Else
+		Return readdir_( dir )
+	End If
 End Function
 End Function
 
 
 Rem
 Rem
 bbdoc: Close a directory
 bbdoc: Close a directory
 End Rem
 End Rem
 Function CloseDir( dir:Byte Ptr )
 Function CloseDir( dir:Byte Ptr )
-	closedir_ dir
+	If MaxIO.ioInitialized Then
+		bmx_blitzio_closeDir(dir)
+	Else
+		closedir_ dir
+	End If
 End Function
 End Function
 
 
 Rem
 Rem

+ 74 - 0
io.mod/common.bmx

@@ -0,0 +1,74 @@
+' Copyright (c) 2020 Bruce A Henderson
+' 
+' This software is provided 'as-is', without any express or implied
+' warranty. In no event will the authors be held liable for any damages
+' arising from the use of this software.
+' 
+' Permission is granted to anyone to use this software for any purpose,
+' including commercial applications, and to alter it and redistribute it
+' freely, subject to the following restrictions:
+' 
+' 1. The origin of this software must not be misrepresented; you must not
+'    claim that you wrote the original software. If you use this software
+'    in a product, an acknowledgment in the product documentation would be
+'    appreciated but is not required.
+' 2. Altered source versions must be plainly marked as such, and must not be
+'    misrepresented as being the original software.
+' 3. This notice may not be removed or altered from any source distribution.
+' 
+SuperStrict
+
+Import Pub.Physfs
+
+Import "../../pub.mod/physfs.mod/physfs/src/*.h"
+Import "glue.c"
+
+
+Extern
+
+	Function bmx_PHYSFS_init:Int()
+	Function bmx_PHYSHS_getLastError:String()
+	Function bmx_PHYSFS_mount:Int(newDir:String, mountPoint:String, appendToPath:Int)
+	Function bmx_PHYSFS_getBaseDir:String()
+	Function bmx_PHYSFS_getPrefDir:String(org:String, app:String)
+	Function bmx_PHYSFS_mountMemory:Int(dirPtr:Byte Ptr, dirLen:Int, newDir:String, mountPoint:String, appendToPath:Int)
+	
+	
+	Function PHYSFS_tell:Long(filePtr:Byte Ptr)
+	Function PHYSFS_seek:Int(filePtr:Byte Ptr, newPos:Long)
+	Function PHYSFS_fileLength:Long(filePtr:Byte Ptr)
+	Function PHYSFS_readBytes:Long(filePtr:Byte Ptr, buf:Byte Ptr, length:ULong)
+	Function PHYSFS_writeBytes:Long(filePtr:Byte Ptr, buf:Byte Ptr, length:ULong)
+	Function PHYSFS_flush:Int(filePtr:Byte Ptr)
+	Function PHYSFS_close:Int(filePtr:Byte Ptr)
+	Function PHYSFS_setBuffer:Int(filePtr:Byte Ptr, bufsize:ULong)
+
+	Function bmx_PHYSFS_openAppend:Byte Ptr(path:String)
+	Function bmx_PHYSFS_openWrite:Byte Ptr(path:String)
+	Function bmx_PHYSFS_openRead:Byte Ptr(path:String)
+		
+	Function bmx_PHYSFS_stat:Int(filename:String, stat:SMaxIO_Stat Var)
+	Function bmx_PHYSFS_delete:Int(path:String)
+	Function bmx_PHYSFS_mkdir:Int(dirName:String)
+	
+	Function bmx_blitzio_readdir:Byte Ptr(dir:String)
+	Function bmx_blitzio_nextFile:String(dir:Byte Ptr)
+	Function bmx_blitzio_closeDir(dir:Byte Ptr)
+	
+End Extern
+
+Struct SMaxIO_Stat
+	Field _filesize:Long
+	Field _modtime:Long
+	Field _createtime:Long
+	Field _accesstime:Long
+	Field _filetype:EMaxIOFileType
+	Field _readonly:Int
+End Struct
+
+Enum EMaxIOFileType:Int
+	REGULAR
+	DIRECTORY
+	SYMLINK
+	OTHER
+End Enum

+ 153 - 0
io.mod/glue.c

@@ -0,0 +1,153 @@
+/*
+  Copyright (c) 2020 Bruce A Henderson
+  
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+  
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+  
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/ 
+#include "physfs.h"
+#include "brl.mod/blitz.mod/blitz.h"
+
+struct MaxFilesEnumeration {
+	char ** files;
+	int index;
+};
+
+int bmx_PHYSFS_init() {
+	return PHYSFS_init(bbArgv0);
+}
+
+BBString * bmx_PHYSHS_getLastError() {
+	int code = PHYSFS_getLastErrorCode();
+	if (code == PHYSFS_ERR_OK) {
+		return &bbEmptyString;
+	}
+	return bbStringToUTF8String(PHYSFS_getErrorByCode(code));
+}
+
+int bmx_PHYSFS_mount(BBString * newDir, BBString * mountPoint, int appendToPath) {
+	char dbuf[1024];
+	size_t dlen = 1024;
+	bbStringToUTF8StringBuffer(newDir, dbuf, &dlen);
+	char mbuf[256];
+	size_t mlen = 256;
+	if (mountPoint != &bbEmptyString) {
+		bbStringToUTF8StringBuffer(mountPoint, mbuf, &mlen);
+		return PHYSFS_mount(dbuf, mbuf, appendToPath);
+	}
+	return PHYSFS_mount(dbuf, NULL, appendToPath);
+}
+
+BBString * bmx_PHYSFS_getBaseDir() {
+	return bbStringFromUTF8String(PHYSFS_getBaseDir());
+}
+
+BBString * bmx_PHYSFS_getPrefDir(BBString * org, BBString * app) {
+	char obuf[128];
+	size_t olen = 128;
+	bbStringToUTF8StringBuffer(org, obuf, &olen);
+
+	char abuf[128];
+	size_t alen = 128;
+	bbStringToUTF8StringBuffer(app, abuf, &alen);
+
+	return bbStringFromUTF8String(PHYSFS_getPrefDir(obuf, abuf));
+}
+
+int bmx_PHYSFS_mountMemory(void * dirPtr, int dirLen, BBString * newDir, BBString * mountPoint, int appendToPath) {
+	char dbuf[1024];
+	size_t dlen = 1024;
+	bbStringToUTF8StringBuffer(newDir, dbuf, &dlen);
+	char mbuf[256];
+	size_t mlen = 256;
+	if (mountPoint != &bbEmptyString) {
+		bbStringToUTF8StringBuffer(mountPoint, mbuf, &mlen);
+		return PHYSFS_mountMemory(dirPtr, dirLen, NULL, dbuf, mbuf, appendToPath);
+	}
+	return PHYSFS_mountMemory(dirPtr, dirLen, NULL, dbuf, NULL, appendToPath);
+}
+
+PHYSFS_File * bmx_PHYSFS_openAppend(BBString * path) {
+	char buf[1024];
+	size_t len = 1024;
+	bbStringToUTF8StringBuffer(path, buf, &len);
+	return PHYSFS_openAppend(buf);
+}
+
+PHYSFS_File * bmx_PHYSFS_openWrite(BBString * path) {
+	char buf[1024];
+	size_t len = 1024;
+	bbStringToUTF8StringBuffer(path, buf, &len);
+	return PHYSFS_openWrite(buf);
+}
+
+PHYSFS_File * bmx_PHYSFS_openRead(BBString * path) {
+	char buf[1024];
+	size_t len = 1024;
+	bbStringToUTF8StringBuffer(path, buf, &len);
+	return PHYSFS_openRead(buf);
+}
+
+int bmx_PHYSFS_stat(BBString * filename, PHYSFS_Stat * stat) {
+	char buf[1024];
+	size_t len = 1024;
+	bbStringToUTF8StringBuffer(filename, buf, &len);
+	return PHYSFS_stat(buf, stat);
+}
+
+int bmx_PHYSFS_delete(BBString * filename) {
+	char buf[1024];
+	size_t len = 1024;
+	bbStringToUTF8StringBuffer(filename, buf, &len);
+	return PHYSFS_delete(buf);
+}
+
+int bmx_PHYSFS_mkdir(BBString * dirName) {
+	char buf[1024];
+	size_t len = 1024;
+	bbStringToUTF8StringBuffer(dirName, buf, &len);
+	return PHYSFS_mkdir(buf);
+}
+
+struct MaxFilesEnumeration * bmx_blitzio_readdir(BBString * dir) {
+	char buf[1024];
+	size_t len = 1024;
+	bbStringToUTF8StringBuffer(dir, buf, &len);
+	
+	char ** files = PHYSFS_enumerateFiles(buf);
+	
+	if (files == NULL)
+		return NULL;
+
+	struct MaxFilesEnumeration * mfe = malloc(sizeof(struct MaxFilesEnumeration));
+	mfe->files = files;
+	mfe->index = 0;
+	
+	return mfe;
+}
+
+BBString * bmx_blitzio_nextFile(struct MaxFilesEnumeration * mfe) {
+	char * f = mfe->files[mfe->index];
+	if (f) {
+		mfe->index++;
+		return bbStringFromUTF8String(f);
+	}
+	return &bbEmptyString;
+}
+
+void bmx_blitzio_closeDir(struct MaxFilesEnumeration * mfe) {
+	PHYSFS_freeList(mfe->files);
+	free(mfe);
+}

+ 191 - 0
io.mod/io.bmx

@@ -0,0 +1,191 @@
+' Copyright (c) 2020 Bruce A Henderson
+' 
+' This software is provided 'as-is', without any express or implied
+' warranty. In no event will the authors be held liable for any damages
+' arising from the use of this software.
+' 
+' Permission is granted to anyone to use this software for any purpose,
+' including commercial applications, and to alter it and redistribute it
+' freely, subject to the following restrictions:
+' 
+' 1. The origin of this software must not be misrepresented; you must not
+'    claim that you wrote the original software. If you use this software
+'    in a product, an acknowledgment in the product documentation would be
+'    appreciated but is not required.
+' 2. Altered source versions must be plainly marked as such, and must not be
+'    misrepresented as being the original software.
+' 3. This notice may not be removed or altered from any source distribution.
+' 
+SuperStrict
+
+Rem
+bbdoc: IO Abstraction
+End Rem
+Module BRL.IO
+
+ModuleInfo "Version: 1.00"
+ModuleInfo "License: zlib/libpng"
+ModuleInfo "Copyright: Bruce A Henderson"
+
+ModuleInfo "History: 1.00"
+ModuleInfo "History: Initial Release."
+
+Import "common.bmx"
+
+Rem
+bbdoc: IO abstraction implementation.
+about: 
+End Rem
+Type MaxIO
+
+	Global ioInitialized:Int
+
+	Rem
+	bbdoc: Initialises the abstraction layer.
+	about: This must be called before any other #MaxIO functions.
+	End Rem
+	Function Init()
+		If Not ioInitialized Then
+			If Not bmx_PHYSFS_init() Then
+				Throw bmx_PHYSHS_getLastError()
+			End If
+		End If
+		ioInitialized = True
+	End Function
+
+	Rem
+	bbdoc: Adds an archive or directory to the search path.
+	about: If this is a duplicate, the entry is not added again, even though the function succeeds.
+	You may not add the same archive to two different mountpoints: duplicate checking is done against the archive and not the mountpoint.
+	
+	When you mount an archive, it is added to a virtual file system...all files in all of the archives are interpolated into a single
+	hierachical file tree. Two archives mounted at the same place (or an archive with files overlapping another mountpoint) may have
+	overlapping files: in such a case, the file earliest in the search path is selected, and the other files are inaccessible to the
+	application. This allows archives to be used to override previous revisions; you can use the mounting mechanism to place archives
+	at a specific point in the file tree and prevent overlap; this is useful for downloadable mods that might trample over application data
+	or each other, for example.
+
+	The mountpoint does not need to exist prior to mounting, which is different than those familiar with the Unix concept of "mounting"
+	may expect. As well, more than one archive can be mounted to the same mountpoint, or mountpoints and archive contents can overlap...the
+	interpolation mechanism still functions as usual.
+	End Rem
+	Function Mount:Int(newDir:String, mountPoint:String = Null, appendToPath:Int = True)
+		Assert ioInitialized Else "MaxIO not initialized"
+		If newDir.StartsWith("incbin::") Then
+			Return MountIncBin(newDir[8..], mountPoint, appendToPath)
+		End If
+		Return bmx_PHYSFS_mount(newDir, mountPoint, appendToPath)
+	End Function
+
+	Rem
+	bbdoc: Adds an incbinned archive to the search path.
+	about: See #Mount for more information.
+	End Rem
+	Function MountIncbin:Int(newDir:String, mountPoint:String = Null, appendToPath:Int = True)
+		Assert ioInitialized Else "MaxIO not initialized"
+		Assert IncbinPtr(newDir) Else "No Incbin for " + newDir
+		Return bmx_PHYSFS_mountMemory(IncbinPtr(newDir), IncbinLen(newDir), newDir, mountPoint, appendToPath)
+	End Function
+
+	Rem
+	bbdoc: Gets the path where the application resides.
+	End Rem
+	Function GetBaseDir:String()
+		Assert ioInitialized Else "MaxIO not initialized"
+		Return bmx_PHYSFS_getBaseDir()
+	End Function
+	
+	Rem
+	bbdoc: Gets the user-and-app-specific path where files can be written.
+	about: Get the "pref dir". This is meant to be where users can write personal files (preferences and save games, etc) that are specific
+	to your application. This directory is unique per user, per application.
+
+	This function will decide the appropriate location in the native filesystem, create the directory if necessary, and return a string in platform-dependent
+	notation, suitable for passing to #SetWriteDir().
+
+	On Windows, this might look like: "C:\\Users\\bob\\AppData\\Roaming\\My Company\\My Program Name"
+
+	On Linux, this might look like: "/home/bob/.local/share/My Program Name"
+
+	On Mac OS X, this might look like: "/Users/bob/Library/Application Support/My Program Name"
+
+	(etc.)
+
+	You should probably use the pref dir for your write dir, and also put it near the beginning of your search path.
+	This finds the correct location for whatever platform, which not only changes between operating systems, but also versions of the same operating system.
+
+	You specify the name of your organization (if it's not a real organization, your name or an Internet domain you own might do) and the name of
+	your application. These should be proper names.
+
+	Both the (org) and (app) strings may become part of a directory name, so please follow these rules:
+
+	Try to use the same org string (including case-sensitivity) for all your applications that use this function.
+	Always use a unique app string for each one, and make sure it never changes for an app once you've decided on it.
+	Unicode characters are legal, as long as it's UTF-8 encoded, but...
+	...only use letters, numbers, and spaces. Avoid punctuation like "Game Name 2: Bad Guy's Revenge!" ... "Game Name 2" is sufficient.
+	
+	> You should assume the path returned by this function is the only safe place to write files.
+	End Rem
+	Function GetPrefDir:String(org:String, app:String)
+		Return bmx_PHYSFS_getPrefDir(org, app)
+	End Function
+
+	Rem
+	bbdoc: Gets various information about a directory or a file.
+	End Rem
+	Function Stat:Int(filename:String, _stat:SMaxIO_Stat Var)
+		Return bmx_PHYSFS_stat(filename, _stat)
+	End Function
+	
+	Rem
+	bbdoc: Deletes a file or directory.
+	about: @path is specified in platform-independent notation in relation to the write dir.
+	
+	A directory must be empty before this call can delete it.
+
+	Deleting a symlink will remove the link, not what it points to, regardless of whether you "permitSymLinks" or not.
+
+	So if you've got the write dir set to "C:\mygame\writedir" and call DeletePath("downloads/maps/level1.map") then the file
+	"C:\mygame\writedir\downloads\maps\level1.map" is removed from the physical filesystem, if it exists and the operating system permits the deletion.
+
+	Note that on Unix systems, deleting a file may be successful, but the actual file won't be removed until all processes that have
+	an open filehandle to it (including your program) close their handles.
+
+	Chances are, the bits that make up the file still exist, they are just made available to be written over at a later point. Don't
+	consider this a security method or anything.
+	End Rem
+	Function DeletePath:Int(path:String)
+		Return bmx_PHYSFS_delete(path)
+	End Function
+	
+	Rem
+	bbdoc: Opens a file for writing.
+	about: Opens a file for writing, in platform-independent notation and in relation to the write dir as the root of the writable filesystem.
+	The specified file is created if it doesn't exist. If it does exist, it is truncated to zero bytes, and the writing offset is set to the start.
+
+	Note that entries that are symlinks are ignored if PermitSymbolicLinks(True) hasn't been called, and opening a symlink with this function will
+	fail in such a case.
+	End Rem
+	Function OpenWrite:Byte Ptr(path:String)
+		Return bmx_PHYSFS_openWrite(path)
+	End Function
+	
+	Rem
+	bbdoc: Closes a file handle.
+	about: This call is capable of failing if the operating system was buffering writes to the physical media, and, now forced to write those
+	changes to physical media, can not store the data for some reason. In such a case, the filehandle stays open. A well-written program
+	should ALWAYS check the return value from the close call in addition to every writing call!
+	End Rem
+	Function Close:Int(filePtr:Byte Ptr)
+		Return PHYSFS_close(filePtr)
+	End Function
+	
+	Rem
+	bbdoc: Creates a directory.
+	about: This is specified in platform-independent notation in relation to the write dir. All missing parent directories are also created if they don't exist.
+	End Rem
+	Function MkDir:Int(dirName:String)
+		Return bmx_PHYSFS_mkdir(dirName)
+	End Function
+	
+End Type

+ 155 - 41
stream.mod/stream.bmx

@@ -35,6 +35,7 @@ ModuleInfo "History: Added LoadString"
 ModuleInfo "History: Added LoadByteArray"
 ModuleInfo "History: Added LoadByteArray"
 ModuleInfo "History: Cleaned up docs a bit"
 ModuleInfo "History: Cleaned up docs a bit"
 
 
+Import BRL.IO
 Import Pub.StdC
 Import Pub.StdC
 
 
 Rem
 Rem
@@ -594,18 +595,14 @@ Type TStreamStream Extends TStreamWrapper
 	
 	
 End Type
 End Type
 
 
-Rem
-bbdoc: Standard C file stream type
-about:
-End Rem
-Type TCStream Extends TStream
+Type TFileStream Extends TStream
 
 
 	Const MODE_READ:Int=1
 	Const MODE_READ:Int=1
 	Const MODE_WRITE:Int=2
 	Const MODE_WRITE:Int=2
 	Const MODE_APPEND:Int = 4
 	Const MODE_APPEND:Int = 4
 	
 	
 	Field _pos:Long,_size:Long,_mode:Int
 	Field _pos:Long,_size:Long,_mode:Int
-	Field _cstream:Byte Ptr
+	Field _stream:Byte Ptr
 
 
 	Method Pos:Long() Override
 	Method Pos:Long() Override
 		Return _pos
 		Return _pos
@@ -614,83 +611,97 @@ Type TCStream Extends TStream
 	Method Size:Long() Override
 	Method Size:Long() Override
 		Return _size
 		Return _size
 	End Method
 	End Method
+	
+	Method Delete()
+		Close
+	End Method
+
+	Function GetMode:String(readable:Int,writeMode:Int, _mode:Int Var)
+		Local Mode$
+		If readable And writeMode = WRITE_MODE_OVERWRITE
+			Mode="r+b"
+			_mode=MODE_READ|MODE_WRITE
+		Else If readable And writeMode = WRITE_MODE_APPEND
+			Mode="a+b"
+			_mode=MODE_READ|MODE_APPEND
+		Else If writeMode = WRITE_MODE_OVERWRITE
+			Mode="wb"
+			_mode=MODE_WRITE
+		Else If writeMode = WRITE_MODE_APPEND
+			Mode="ab"
+			_mode=MODE_APPEND
+		Else
+			Mode="rb"
+			_mode=MODE_READ
+		EndIf
+		Return Mode
+	End Function
+
+End Type
+
+Rem
+bbdoc: Standard C file stream type
+about:
+End Rem
+Type TCStream Extends TFileStream
 
 
 	Method Seek:Long( pos:Long, whence:Int = SEEK_SET_ ) Override
 	Method Seek:Long( pos:Long, whence:Int = SEEK_SET_ ) Override
-		Assert _cstream Else "Attempt to seek closed stream"
-		fseek_ _cstream,pos,whence
-		_pos=ftell_( _cstream )
+		Assert _stream Else "Attempt to seek closed stream"
+		fseek_ _stream,pos,whence
+		_pos=ftell_( _stream )
 		Return _pos
 		Return _pos
 	End Method
 	End Method
 
 
 	Method Read:Long( buf:Byte Ptr,count:Long ) Override
 	Method Read:Long( buf:Byte Ptr,count:Long ) Override
-		Assert _cstream Else "Attempt to read from closed stream"
+		Assert _stream Else "Attempt to read from closed stream"
 		Assert _mode & MODE_READ Else "Attempt to read from write-only stream"
 		Assert _mode & MODE_READ Else "Attempt to read from write-only stream"
-		count=fread_( buf,1,count,_cstream )	
+		count=fread_( buf,1,count,_stream )	
 		_pos:+count
 		_pos:+count
 		Return count
 		Return count
 	End Method
 	End Method
 
 
 	Method Write:Long( buf:Byte Ptr,count:Long ) Override
 	Method Write:Long( buf:Byte Ptr,count:Long ) Override
-		Assert _cstream Else "Attempt to write to closed stream"
+		Assert _stream Else "Attempt to write to closed stream"
 		Assert _mode & (MODE_WRITE | MODE_APPEND) Else "Attempt to write to read-only stream"
 		Assert _mode & (MODE_WRITE | MODE_APPEND) Else "Attempt to write to read-only stream"
-		count=fwrite_( buf,1,count,_cstream )
+		count=fwrite_( buf,1,count,_stream )
 		_pos:+count
 		_pos:+count
 		If _pos>_size _size=_pos
 		If _pos>_size _size=_pos
 		Return count
 		Return count
 	End Method
 	End Method
 
 
 	Method Flush() Override
 	Method Flush() Override
-		If _cstream fflush_ _cstream
+		If _stream fflush_ _stream
 	End Method
 	End Method
 
 
 	Method Close() Override
 	Method Close() Override
-		If Not _cstream Return
+		If Not _stream Return
 		Flush
 		Flush
-		fclose_ _cstream
+		fclose_ _stream
 		_pos=0
 		_pos=0
 		_size=0
 		_size=0
-		_cstream=Null
+		_stream=Null
 	End Method
 	End Method
 
 
 	Method SetSize:Int(size:Long) Override
 	Method SetSize:Int(size:Long) Override
-		If Not _cstream Return 0
+		If Not _stream Return 0
 		Flush()
 		Flush()
 		If _size > size Then
 		If _size > size Then
 			Seek(size)
 			Seek(size)
 		End If
 		End If
-		Local res:Int = ftruncate_(_cstream, size)
+		Local res:Int = ftruncate_(_stream, size)
 		Flush()
 		Flush()
 		If Not res Then
 		If Not res Then
 			_size = size
 			_size = size
 		End If
 		End If
 		Return res = 0
 		Return res = 0
 	End Method
 	End Method
-	
-	Method Delete()
-		Close
-	End Method
 
 
 	Rem
 	Rem
 	bbdoc: Create a TCStream from a 'C' filename
 	bbdoc: Create a TCStream from a 'C' filename
 	End Rem
 	End Rem
 	Function OpenFile:TCStream( path$,readable:Int,writeMode:Int )
 	Function OpenFile:TCStream( path$,readable:Int,writeMode:Int )
 		Local Mode$,_mode:Int
 		Local Mode$,_mode:Int
-		If readable And writeMode = WRITE_MODE_OVERWRITE
-			Mode="r+b"
-			_mode=MODE_READ|MODE_WRITE
-		Else If readable And writeMode = WRITE_MODE_APPEND
-			Mode="a+b"
-			_mode=MODE_READ|MODE_APPEND
-		Else If writeMode = WRITE_MODE_OVERWRITE
-			Mode="wb"
-			_mode=MODE_WRITE
-		Else If writeMode = WRITE_MODE_APPEND
-			Mode="ab"
-			_mode=MODE_APPEND
-		Else
-			Mode="rb"
-			_mode=MODE_READ
-		EndIf
+		Mode = GetMode(readable, writeMode, _mode)
 		path=path.Replace( "\","/" )
 		path=path.Replace( "\","/" )
 		Local cstream:Byte Ptr=fopen_( path,Mode )
 		Local cstream:Byte Ptr=fopen_( path,Mode )
 ?Linux
 ?Linux
@@ -707,7 +718,7 @@ Type TCStream Extends TStream
 	end rem
 	end rem
 	Function CreateWithCStream:TCStream( cstream:Byte Ptr,Mode:Int )
 	Function CreateWithCStream:TCStream( cstream:Byte Ptr,Mode:Int )
 		Local stream:TCStream=New TCStream
 		Local stream:TCStream=New TCStream
-		stream._cstream=cstream
+		stream._stream=cstream
 		stream._pos=ftell_( cstream )
 		stream._pos=ftell_( cstream )
 		fseek_ cstream,0,SEEK_END_
 		fseek_ cstream,0,SEEK_END_
 		stream._size=ftell_( cstream )
 		stream._size=ftell_( cstream )
@@ -780,7 +791,13 @@ Function OpenStream:TStream( url:Object,readable:Int=True,writeMode:Int=WRITE_MO
 	Local str$=String( url ),proto$,path$
 	Local str$=String( url ),proto$,path$
 	If str
 	If str
 		Local i:Int=str.Find( "::",0 )
 		Local i:Int=str.Find( "::",0 )
-		If i=-1 Return TCStream.OpenFile( str,readable,writeMode )
+		If i=-1 Then
+			If MaxIO.ioInitialized Then
+				Return TIOStream.OpenFile(str, readable, writemode)
+			Else
+				Return TCStream.OpenFile( str,readable,writeMode )
+			End If
+		End If
 		proto$=str[..i].ToLower()
 		proto$=str[..i].ToLower()
 		path$=str[i+2..]
 		path$=str[i+2..]
 	EndIf
 	EndIf
@@ -1201,3 +1218,100 @@ bbdoc: Opens a file for appending with all output operations writing data at the
 about: Repositioning operations such as #Seek affects the next input operations, but output operations move the position back to the end of file.
 about: Repositioning operations such as #Seek affects the next input operations, but output operations move the position back to the end of file.
 End Rem
 End Rem
 Const WRITE_MODE_APPEND:Int = 2
 Const WRITE_MODE_APPEND:Int = 2
+
+
+Type TIOStream Extends TFileStream
+
+	Method Pos:Long() Override
+		Return _pos
+	End Method
+
+	Method Size:Long() Override
+		Return _size
+	End Method
+
+	Method Seek:Long( pos:Long, whence:Int = SEEK_SET_ ) Override
+		Assert _stream Else "Attempt to seek closed stream"
+		'fseek_ _cstream,pos,whence
+		Local newPos:Long = pos
+		If whence = SEEK_END_ Then
+			newPos = _size
+		Else If whence = SEEK_CUR_ Then
+			newPos = _pos + pos
+		End If
+		PHYSFS_seek(_stream, newPos)
+
+		_pos = PHYSFS_tell(_stream)
+		Return _pos
+	End Method
+
+	Method Read:Long( buf:Byte Ptr,count:Long ) Override
+		Assert _stream Else "Attempt to read from closed stream"
+		Assert _mode & MODE_READ Else "Attempt to read from write-only stream"
+		count=PHYSFS_readBytes(_stream, buf,ULong(count))	
+		_pos:+count
+		Return count
+	End Method
+
+	Method Write:Long( buf:Byte Ptr,count:Long ) Override
+		Assert _stream Else "Attempt to write to closed stream"
+		Assert _mode & (MODE_WRITE | MODE_APPEND) Else "Attempt to write to read-only stream"
+		count=PHYSFS_writeBytes(_stream, buf, ULong(count))
+		_pos:+count
+		If _pos>_size _size=_pos
+		Return count
+	End Method
+
+	Method Flush() Override
+		If _stream PHYSFS_flush(_stream)
+	End Method
+
+	Method Close() Override
+		If Not _stream Return
+		Flush
+		PHYSFS_close(_stream)
+		_pos=0
+		_size=0
+		_stream=Null
+	End Method
+
+	Method SetSize:Int(size:Long) Override
+		Return 0
+	End Method
+
+	Function OpenFile:TIOStream( path$,readable:Int,writeMode:Int )
+		Local Mode$,_mode:Int
+		Mode = GetMode(readable, writeMode, _mode)
+		path=path.Replace( "\","/" )
+		
+		Local stream:Byte Ptr
+		If _mode & MODE_APPEND Then
+			stream = bmx_PHYSFS_openAppend(path)
+		Else If _mode & MODE_WRITE Then
+			stream = bmx_PHYSFS_openWrite(path)
+		Else
+			stream = bmx_PHYSFS_openRead(path)
+			If stream
+				PHYSFS_setBuffer(stream, 4096)
+			End If
+		End If
+
+'?Linux
+'		If (Not cstream) And (Not writeMode)
+'			path=CasedFileName(path)
+'			If path cstream=fopen_( path,Mode )
+'		EndIf
+'?
+		If stream Return CreateWithIOStream( stream,_mode )
+	End Function
+
+	Function CreateWithIOStream:TIOStream( _stream:Byte Ptr,Mode:Int )
+		Local stream:TIOStream=New TIOStream
+		stream._stream=_stream
+		stream._pos=PHYSFS_tell( _stream )
+		stream._size=PHYSFS_fileLength(_stream)
+		stream._mode=Mode
+		Return stream
+	End Function
+
+End Type