Tag: purebasic

(PB) Loading Assets, the simple way.

Posted by on January 23, 2012

Intro

Assets are an essential part of most games, how you manage them determines whether you spend more time working on them than dealing with them.

For small to medium games, loading an entire directory and having it referenced to a Map is the ideal solution. It’s both flexible and simple.

The following code allows you to do just this:

Prototype.i LOAD_DIRECTORY_CALLBACK( Map Assets.i(), directory.s, extension.s, name.s, userDefined.i = #Null )
 
Procedure.i LoadDirectory( Map assets.i(), directory.s, extension.s, *callback.LOAD_DIRECTORY_CALLBACK, userDefined.i = #Null )
 
	Define.i dir = ExamineDirectory( #PB_Any, directory, "*" + extension )
	If IsDirectory( dir )
 
		Define.i count = 0
		Define.s name = ""
 
		While NextDirectoryEntry( dir )
			If DirectoryEntryType( dir ) = #PB_DirectoryEntry_File
				name = DirectoryEntryName( dir );
				assets( ReplaceString( name, extension, "" ) ) = *callback( assets(), directory, extension, name, userDefined );LoadSound( #PB_Any, directory + "\" + name )
				count + 1
			EndIf
		Wend
 
		FinishDirectory( dir )
	EndIf
	ProcedureReturn count
 
EndProcedure

To use this code you must define a procedure of your own, this procedure is going to be called on each file to be loaded; you’ll have to load and process the file in this function.

Example use:

Global NewMap sprite.i()
Procedure.i callback_loadsprites( Map Assets.i(), directory.s, extension.s, name.s, userDefined.i = #Null )
	ProcedureReturn LoadSprite( #PB_Any, directory + "\" + name, userDefined )
EndProcedure
 
LoadDirectory( sprite(), "sprites", ".png", @callback_loadsprites() )

The example will attempt to scan through the directory “sprites”, it will execute your callback on each png file it finds inside the aforementioned directory, furthermore it’ll reference the filename (without extension) to the sprite() Map, so when you need a handle for the sprite called “fire.png” you’d just use sprite(“fire”) to obtain it.

Since the files are referenced to a Map by their actual name, you could easily implement a scripting system or any other dynamic management solution for your assets without much hassle.

An interesting part of the code is the return value, it’s actually the number of assets found (not the ones loaded, since your callback could choose not to load a certain file, for instance).

There are several limitations to this simple implementation, which is why I mentioned “small games” – There is no directory recursion, no advanced filtering options, no way to parallel the process on a separate thread and there’s no error handling at the moment.

However, it’s still very useful and I highly recommend you try it out, while it’s not a novel aproach, it’s always been the method I used to load my assets and it just works.

Have fun!

(PB) String Between Characters

Posted by on April 26, 2011

This is a small purebasic function used to retrieve a string between two known characters, it also allows you to provide a starting position.

Procedure.s str_between_char( *Source.CHARACTER, First.C, Last.C, StartAt.i = 0 )
	Define.i dwLength
 
	*Source + (StartAt * SizeOf(CHARACTER))
	Repeat
		If *Source\c = First ; If this character matches the First character
			*Source + SizeOf(CHARACTER)
			dwLength = *Source
			Repeat ; Repeat until we find an occurrance with the Last character
				*Source + SizeOf(CHARACTER)
			Until *Source\c = Last
			dwLength = (*Source - dwLength)
			ProcedureReturn PeekS( *Source - dwLength, dwLength ) ; Peek the output string
			Break
		EndIf
		*Source + SizeOf(CHARACTER)
	Until *Source\c = #Null
 
EndProcedure

Example use:

Define.s str 		= "dd(hallo)xyz(a)"
Define.s result 	= str_between_char(@str, '(', ')', 2)
Debug result

If you’re working on a parser or similar project this will sure come in handy.
Enjoy!

PB Parallel Port Library

Posted by on January 12, 2011

What is it:

This is a minimalist  library based on the inpout32 DLL

Basically I wrapped the original library and added a whole bunch of useful constants to easily access the Control and Status registers while keeping things clean and simple.

This library is currently being used on a side project I’ve been working on with a friend, since he doesn’t have any μController experience we decided to use LPT for the time being.

The code:

 
; 	Minimalist Library for Parallel port access based on inpout32.dll 
;		by Gustavo J. Fiorenza AKA GuShH (info@gushh.net - info@gushh.com.ar)
;		Version 1.0 - 11/01/2011
 
EnableExplicit
 
; Default LPT Addresses.
#PARALLEL_PORT_LPT1						= $3BC
#PARALLEL_PORT_LPT2						= $378
#PARALLEL_PORT_LPT3						= $278
 
; Register offsets.
#PARALLEL_PORT_STATUS						= $01
#PARALLEL_PORT_CONTROL						= $02
 
; Shared with Data Register.
#PARALLEL_PORT_OFF						= $00
#PARALLEL_PORT_BIT0						= $01
#PARALLEL_PORT_BIT1						= $02
#PARALLEL_PORT_BIT2						= $04
#PARALLEL_PORT_BIT3						= $08
#PARALLEL_PORT_BIT4						= $10
#PARALLEL_PORT_BIT5						= $20
#PARALLEL_PORT_BIT6						= $40
#PARALLEL_PORT_BIT7						= $80
 
XIncludeFile "ParallelPort_Constants.pbi" 	; All of the helper, non-essential constants are defined here.
 
 
Structure PARALLEL_PORT
	Handle.i
	Port.i
	LastData.i
EndStructure
 
 
;- Instance construction and destruction
 
Procedure.i ParallelPort_Create( Port.i = #PARALLEL_PORT_LPT2 )
 
	Define.PARALLEL_PORT *this = AllocateMemory( SizeOf(PARALLEL_PORT) )
	If *this
 
		*this\Port 	= port
		*this\Handle 	= OpenLibrary( #PB_Any, "inpout32.dll" )
 
		If IsLibrary( *this\Handle )
			ProcedureReturn *this
		Else
			Debug "Couldn't load inpout32.dll"
			FreeMemory(*this)
			ProcedureReturn #Null
		EndIf
 
	EndIf
 
EndProcedure
 
Procedure.i ParallelPort_Destroy( *this.PARALLEL_PORT )
 
	If *this
 
		If IsLibrary( *this\Handle )
			CloseLibrary( *this\Handle )
		EndIf
 
		FreeMemory( *this )
		*this = #Null
		ProcedureReturn *this
 
	EndIf
 
EndProcedure
 
;- Communication Functions
 
Procedure.i ParallelPort_Out( *this.PARALLEL_PORT, Bits.w = $00 )
	If *this
		*this\LastData = Bits & $FF
		ProcedureReturn CallFunction( *this\handle, "Out32", *this\Port, Bits )
	EndIf
EndProcedure
 
Procedure.i ParallelPort_In( *this.PARALLEL_PORT, Type.i )
	If *this
		ProcedureReturn CallFunction( *this\handle, "Inp32", *this\Port + Type )
	EndIf
EndProcedure
 
;- Getter Functions
 
Procedure.i ParallelPort_GetHandle( *this.PARALLEL_PORT )
	If *this
		ProcedureReturn *this\Handle
	EndIf
EndProcedure
 
Procedure.i ParallelPort_GetPort( *this.PARALLEL_PORT )
	If *this
		ProcedureReturn *this\Port
	EndIf
EndProcedure
 
Procedure.i ParallelPort_GetLastData( *this.PARALLEL_PORT )
	If *this
		ProcedureReturn *this\LastData
	EndIf
EndProcedure
 
;- Helper Functions
 
Procedure.i ParallelPort_Clear( *this.PARALLEL_PORT )
	If *this
		ParallelPort_Out( *this ) ; The default data parameter is $00
	EndIf
EndProcedure

And the constants include:

 
; Read Only Status Register.
#PARALLEL_PORT_STATUS_IRQ		= #PARALLEL_PORT_BIT2
 
#PARALLEL_PORT_STATUS_ERROR		= #PARALLEL_PORT_BIT3
#PARALLEL_PORT_STATUS_SELECT		= #PARALLEL_PORT_BIT4
#PARALLEL_PORT_STATUS_PAPEROUT		= #PARALLEL_PORT_BIT5
#PARALLEL_PORT_STATUS_ACK		= #PARALLEL_PORT_BIT6
#PARALLEL_PORT_STATUS_BUSY		= #PARALLEL_PORT_BIT7
 
; Read / Write Control Register.
#PARALLEL_PORT_CONTROL_STROBE		= #PARALLEL_PORT_BIT0
#PARALLEL_PORT_CONTROL_LINEFEED		= #PARALLEL_PORT_BIT1
#PARALLEL_PORT_CONTROL_RESET		= #PARALLEL_PORT_BIT2 ; AKA Initialize Printer OR Init.
#PARALLEL_PORT_CONTROL_SELECT		= #PARALLEL_PORT_BIT3
 
; These constants are icluded for completeness. All pin numbers are relative to D-Type 25, Centronics pins are between parentheses.
 
#PARALLEL_PORT_D0	=	#PARALLEL_PORT_BIT0			; PIN2
#PARALLEL_PORT_D1	=	#PARALLEL_PORT_BIT1			; PIN3
#PARALLEL_PORT_D2	=	#PARALLEL_PORT_BIT2			; PIN4
#PARALLEL_PORT_D3	=	#PARALLEL_PORT_BIT3			; PIN5
#PARALLEL_PORT_D4	=	#PARALLEL_PORT_BIT4			; PIN6
#PARALLEL_PORT_D5	=	#PARALLEL_PORT_BIT5			; PIN7
#PARALLEL_PORT_D6	=	#PARALLEL_PORT_BIT6			; PIN8
#PARALLEL_PORT_D7	=	#PARALLEL_PORT_BIT7			; PIN9
 
#PARALLEL_PORT_C0	=	#PARALLEL_PORT_CONTROL_STROBE		; PIN 1
#PARALLEL_PORT_C1	=	#PARALLEL_PORT_CONTROL_LINEFEED		; PIN 14
#PARALLEL_PORT_C2	=	#PARALLEL_PORT_CONTROL_RESET		; PIN 16	(31)
#PARALLEL_PORT_C3	=	#PARALLEL_PORT_CONTROL_SELECT		; PIN 17	(36)
 
#PARALLEL_PORT_S3	=	#PARALLEL_PORT_STATUS_ERROR		; PIN 15	(32)
#PARALLEL_PORT_S4	=	#PARALLEL_PORT_STATUS_SELECT		; PIN 13
#PARALLEL_PORT_S5	=	#PARALLEL_PORT_STATUS_PAPEROUT		; PIN 12
#PARALLEL_PORT_S6	=	#PARALLEL_PORT_STATUS_ACK		; PIN 10
#PARALLEL_PORT_S7	=	#PARALLEL_PORT_STATUS_BUSY		; PIN 11
 
; PINS 18-25 (19-30) Are all tied to GND.

The hardware:

You don’t need any special hardware other than a Centronics or similar cable and a PC with a Parallel port (I’m sure some of you keep older PCs around for a good reason!) — Aside from this if you’re planning on running the examples you might want to get some LEDs and Switches.

Something to download:

The entire sources and support files including the examples can be found Here. Remember to exit the demo programs with the ESC key so the program gets a chance to reset the output bits.

Useful companion:

During development you may want to keep an eye on the status of each pin, however not everyone has a breakout board for this particular interface so you may want to use a software version of this concept instead, one of my favourite ones is LPT.exe — It works fairly well and there are several ports to different languages in case you’re interested in modifying it to suit your own needs.

Closing up:

That’s all for now, hopefully I’ll be able to finish the project and post some of the code here. Hint: it involves controlling unipolar steppers, computer vision and webcams!

Can you guess what it is?

Cheers.

ICMP Ping Routine (Windows only)

Posted by on June 4, 2010

Heres my ping routine, It’s the weapon of choice for checking online status, update servers, or just plain pinging an IP for whatever reason happens to float your boat!

EnableExplicit
Procedure.i NetPing( IP_Address.s = "209.85.225.105", Timeout.i = 5000 ) ; Timeout in Milliseconds, Defaults to 5 seconds.
 
  ;Returns => 0 Upon pinging. Otherwise returns < 0 -- The actual return value is the roundtrip time in milliseconds.
 
  Define.i hIcmpFile, dwRetVal			= -1
  Define.s DataBuffer 				= "PING"
  Define.i ReplyBufferSize 			= SizeOf(ICMP_ECHO_REPLY) + Len(DataBuffer) + SizeOf(character)
  Define.ICMP_ECHO_REPLY *ReplyBuffer 		= AllocateMemory(ReplyBufferSize)
 
  If ( *ReplyBuffer )
 
	  hIcmpFile = IcmpCreateFile_()
 
	  If hIcmpFile
 
		 If IcmpSendEcho_( hIcmpFile, inet_addr_(IP_Address), @DataBuffer, Len(DataBuffer), #Null, *ReplyBuffer, ReplyBufferSize + SizeOf(ICMP_ECHO_REPLY), Timeout )
			dwRetVal = *ReplyBuffer\RoundTripTime
		 EndIf
 
		 IcmpCloseHandle_( hIcmpFile )
 
	  Else
	  	Debug "NetPing -- IcmpCreateFile returned false."
	  EndIf
 
	  FreeMemory( *ReplyBuffer )
 
  Else
  	Debug "NetPing -- Couldn't allocate memory."
  EndIf
 
  ProcedureReturn dwRetVal
 
EndProcedure

Enjoy!

FileToMemory – MemoryToFile

Posted by on April 6, 2010

I haven’t had much time to write, so I figured I could at least share some old -yet useful- PB code in the meantime, as promised.

Quite often you’ll need to load a file right into memory or dump memory into a file, these are relatively trivial tasks but I’m sure someone will eventually benefit from the functions, so here we go:

; Be careful trying to read big files into memory, always check the available RAM is enough before doing anything crazy.
Procedure.i FileToMemory( File.s ) ; Returns memory handle if the file was properly loaded, otherwise 0.
 ; Sanitize filename using your own routine.
	Define.i fileHandle	=	ReadFile( #PB_Any, File )
 
	If IsFile(fileHandle)
		Define.i fileSize = Lof(fileHandle)
 
		If fileSize
			Define.i fileBuffer = AllocateMemory( fileSize )
 
			If fileBuffer
 
				If ReadData( fileHandle,  fileBuffer, fileSize ) = fileSize
 
					CloseFile( fileHandle )
					ProcedureReturn fileBuffer
 
				Else
					FreeMemory(fileBuffer)
 
				EndIf
 
			EndIf
		EndIf
 
		CloseFile(fileHandle)
	EndIf
 
	ProcedureReturn #Null
 
EndProcedure
Procedure.i MemoryToFile( Memory.i, File.s ) ; Returns the amount of written data in bytes, otherwise 0.
 
	If Memory
		If File ; Sanitize filename using your own routine.
 
			Define.i MemorySize = MemorySize(Memory)
 
			If MemorySize > 0
 
				Define.i fileHandle = CreateFile( #PB_Any, File )
 
				If IsFile( fileHandle )
 
					Define.i DataWritten = WriteData( fileHandle, Memory, MemorySize )
					CloseFile( fileHandle )
 
					ProcedureReturn DataWritten
 
				EndIf
 
			EndIf
 
		EndIf
	EndIf
 
	ProcedureReturn #Null
 
EndProcedure

Filename sanitation was not included since this is often part of a separate lib.

Quite frankly functions like these should be part of the official libraries, seeing as how often they’re used… I really do think they should focus on adding new functionality to the language — I hear a rant coming!

Well, not today. Let’s keep it simple.

Cheers!