Simple abstract stream

BlitzMax Forums/BlitzMax Programming/Simple abstract stream

JoshK(Posted 2007) [#1]
Here is a very simple abstract stream type. You can simply go ReadFile("abstract::readme.txt") and the stream will return any file in any subfolder called "readme.txt".

This allows you to load media and other files without knowing exactly where they are in your directories.

The initialization uses a TMap, so it should be very fast to startup and retrieve files.

Implementing a package file system in the RegisterAbstractPath() routine would make this much more powerful.

Strict

Private
Global AbstractFileMap:TMap=New TMap
Public

Type TAbstractStreamFactory Extends TStreamFactory
	
	Method CreateStream:TStream( url:Object,proto$,path$,readable,writeable )
		If proto<>"abstract" Return Null
		Local filename$
		Local p
		filename=Lower(StripDir(String(url)))
		p=Instr(filename,"::")
		If p filename=Right(filename,Len(filename)-p-1)
		If filename="" Return Null
		filename=String(MapValueForKey(AbstractFileMap,filename))
		If filename<>"" Return ReadStream(filename)
	EndMethod
	
EndType

New TAbstractStreamFactory

Function RegisterAbstractPath(filepath$)	
	Local name$,filename$
	Local d
	d=ReadDir(filepath)
	If Not d Return
	If Right(filepath,1)="/" filepath=Left(filepath,Len(filepath)-1)
	If Right(filepath,1)="\" filepath=Left(filepath,Len(filepath)-1)
	Repeat
		filename$=NextFile(d)		
		Select FileType(filepath+"/"+filename)
			Case 0 Exit
			Case 1
				name=Lower(StripDir(filename))
				If MapValueForKey(AbstractFileMap,name) Continue
				MapInsert AbstractFileMap,name,RealPath(filepath+"/"+filename)
			Case 2
				If filename<>"." And filename<>".."
					RegisterAbstractPath(filepath+"/"+filename)
				EndIf
		EndSelect
	Forever
	CloseDir d
EndFunction


'-----------------------------------------------
' Test Program
'-----------------------------------------------

Local stream:TStream

RegisterAbstractPath(AppDir)

stream=ReadFile("abstract::readme.txt")
If Not stream
	Notify "Failed to open abstract stream ~qreadme.txt~q.",1
	End
EndIf

While Not stream.eof()
	Notify stream.readline()
Wend

End



JoshK(Posted 2007) [#2]
This version adds support for zip packages. So you can retrieve a zipped file anywhere in the subdirectory by just specifying the file name and extension. Perfect for loading media:
Import koriolis.zipstream
Import gman.zipengine

Private
Global AbstractFileMap:TMap=New TMap
Public

Type TAbstractStreamFactory Extends TStreamFactory
	
	Method CreateStream:TStream( url:Object,proto$,path$,readable,writeable )
		If proto<>"abstract" Return Null
		Local filename$
		Local p
		filename=Lower(StripDir(String(url)))
		p=Instr(filename,"::")
		If p filename=Right(filename,Len(filename)-p-1)
		If filename="" Return Null
		filename=String(MapValueForKey(AbstractFileMap,filename))
		If filename="" Return Null
		Return ReadStream(filename)
	EndMethod
	
EndType

New TAbstractStreamFactory

Function RegisterAbstractPath(filepath$)	
	Local name$,filename$
	Local d
	Local zrObject:ZipReader
	Local packagefile$
	d=ReadDir(filepath)
	If Not d Return
	If Right(filepath,1)="/" filepath=Left(filepath,Len(filepath)-1)
	If Right(filepath,1)="\" filepath=Left(filepath,Len(filepath)-1)
	Repeat
		filename$=NextFile(d)
		If filename="" Exit
		Select FileType(filepath+"/"+filename)
			Case 0 Exit
			Case 1
				name=Lower(StripDir(filename))
				If MapValueForKey(AbstractFileMap,name) Continue
				MapInsert AbstractFileMap,name,RealPath(filepath+"/"+filename)
				
				'Zip stream support
				Select Lower(ExtractExt(filename))
				Case "zip","pk3"
					packagefile=filename
					zrObject=New ZipReader
					If zrObject.OpenZip(filename)
						For Local i:Int=0 To zrObject.getFileCount()-1
							filename$=zrObject.getFileInfo(i).zipFileName
							If ExtractExt(filename)<>""
								name=Lower(StripDir(filename))
								filename="zip::"+packagefile+"//"+filename								
								If MapValueForKey(AbstractFileMap,name) Continue
								MapInsert AbstractFileMap,name,filename
							EndIf
						Next
						zrObject.CloseZip()
					EndIf
				EndSelect
				
			Case 2
				If filename<>"." And filename<>".."
					RegisterAbstractPath(filepath+"/"+filename)
				EndIf
		EndSelect
	Forever
	CloseDir d
EndFunction


'-----------------------------------------------
' Test Program
'-----------------------------------------------

Local stream:TStream

RegisterAbstractPath(AppDir)

stream=ReadFile("abstract::readme.txt")
If Not stream
	Notify "Failed to open abstract stream ~qreadme~q.",1
	End
EndIf

While Not stream.eof()
	Notify stream.readline()
Wend

End