Added 'PublicDataStream.ToDataBuffer', etc.

Strict

Public

' Preprocessor related:
' Nothing so far.

' Imports (Public):
Import regal.byteorder

' BRL:
Import brl.stream

' Imports (Private):
Private

Import regal.util

' BRL:
Import brl.databuffer

Public

' Classes:
Class PublicDataStream Extends Stream Implements IOnLoadDataComplete
	' Constant variable(s):
	Const NOLIMIT:Int = 0 ' -1

	' Defaults:
	Const Default_ResizeScalar:Float = 1.5 ' 2.0

	' Booleans / Flags:
	Const Default_BigEndianStorage:Bool = False

	' Constructor(s) (Public):
	Method New(Size:Int, BigEndianStorage:Bool=Default_BigEndianStorage, Resizable:Bool=True, SizeLimit:Int=NOLIMIT)
		GenerateBuffer(Size)

		Self.BigEndianStorage = BigEndianStorage
		Self.ShouldResize = Resizable
		Self.SizeLimit = SizeLimit
	End

	Method New(B:DataBuffer, Length:Int, Offset:Int=0, Copy:Bool=False, BigEndianStorage:Bool=Default_BigEndianStorage, Resizable:Bool=True, SizeLimit:Int=NOLIMIT)
		If (Copy) Then
			GenerateBuffer(B)

			OwnsBuffer = True
		Else
			Self.Data = B
		Endif

		Self.Offset = Offset
		Self._Length = Length

		Self.RawSize = Length

		Self.BigEndianStorage = BigEndianStorage
		Self.ShouldResize = Resizable
		Self.SizeLimit = SizeLimit
	End

	Method New(Path:String, BigEndianStorage:Bool=Default_BigEndianStorage, Async:Bool=False)
		If (Not Async) Then
			OnLoadDataComplete(DataBuffer.Load(Path), Path)
		Else
			DataBuffer.LoadAsync(Path, Self)
		Endif

		Self.BigEndianStorage = BigEndianStorage
	End

	' Constructor(s) (Protected):
	Protected

	Method GenerateBuffer:Void(Size:Int)
		Self.Data = New DataBuffer(Size)
		Self.RawSize = Size
		Self.OwnsBuffer = True

		Return
	End

	' This will copy the contents of 'B' into an internally managed buffer.
	Method GenerateBuffer:Void(B:DataBuffer)
		GenerateBuffer(B.Length)

		B.CopyBytes(0, Data, 0, Data.Length)

		Return
	End

	Public

	' Destructor(s) (Protected):
	Protected

	Method DestructDetails:Void()
		Self.Offset = 0
		Self._Position = 0

		Return
	End

	Public

	' Destructor(s) (Public):
	Method FreeBuffer:Void(DiscardBuffer:Bool=True)
		If (Data <> Null) Then
			If (DiscardBuffer) Then
				Data.Discard()
			Endif

			Data = Null
		Endif

		OwnsBuffer = False

		RawSize = 0

		Return
	End

	Method Close:Void()
		FreeBuffer(OwnsBuffer)
		DestructDetails()

		Return
	End

	' The resulting 'DataBuffer' represents the 'Data'
	' property before destruction takes place.
	Method CloseWithoutBuffer:DataBuffer()
		Local OutputBuffer:= Data

		FreeBuffer(False)
		DestructDetails()

		Return OutputBuffer
	End

	' Methods:

	' This creates a 'DataBuffer' by copying 'Data' based on the contents described by this point.
	' This is useful for handling storage semantics; copies.
	Method ToDataBuffer:DataBuffer()
		Local outBuffer:= New DataBuffer(Length)

		Data.CopyBytes(Offset, outBuffer, 0, Length)

		Return outBuffer
	End

	Method Seek:Int(Input:Int=0)
		Self._Position = Clamp(Input, 0, DataLength)

		Return Self._Position
	End

	Method Reset:Void()
		Seek()
		ResetLength()

		Return
	End

	' This states if the number of bytes specified may be read safely.
	Method WillOverReach:Bool(Bytes:Int)
		Return ReadWillOverReach(Bytes)
	End

	Method ReadWillOverReach:Bool(Bytes:Int)
		Return (Position+Bytes > Length)
	End

	Method WriteWillOverReach:Bool(Bytes:Int)
		Return (Position+Bytes > DataLength) ' Length
	End

	Method ReadShort:Int()
		If (BigEndianStorage) Then
			Return NToHS(Super.ReadShort())
		Endif

		Return Super.ReadShort()
	End

	Method ReadInt:Int()
		If (BigEndianStorage) Then
			Return NToHL(Super.ReadInt())
		Endif

		Return Super.ReadInt()
	End

	Method ReadFloat:Float()
		If (BigEndianStorage) Then
			Return NToHF(Super.ReadInt())
		Endif

		Return Super.ReadFloat()
	End

	Method WriteShort:Void(Value:Int)
		If (BigEndianStorage) Then
			Super.WriteShort(HToNS(Value))
		Else
			Super.WriteShort(Value)
		Endif

		Return
	End

	Method WriteInt:Void(Value:Int)
		If (BigEndianStorage) Then
			Super.WriteInt(HToNL(Value))
		Else
			Super.WriteInt(Value)
		Endif

		Return
	End

	Method WriteFloat:Void(Value:Float)
		If (BigEndianStorage) Then
			Super.WriteInt(HToNF(Value))
		Else
			Super.WriteFloat(Value)
		Endif

		Return
	End

	Method Read:Int(Buf:DataBuffer, Offset:Int, Count:Int)
		If (WillOverReach(Count)) Then
			Count = Self.BytesLeft
		Endif

		#Rem
			For Local Index:= 0 Until Count
				Buf.PokeByte(Offset+Index, Self.Data.PeekByte(Offset+Position+Index))
			Next
		#End

		Self.Data.CopyBytes(Self.DataOffset, Buf, Offset, Count)

		Self._Position += Count

		Return Count
	End

	Method Write:Int(Buf:DataBuffer, Offset:Int, Count:Int)
		Local NewPosition:= (Count+Position)

		If (NewPosition > DataLength) Then
			If (ShouldResize) Then
				AutoResize(Count)
			Else
				Count = (DataLength-Position) ' Max(..., 0)
			Endif
		Endif

		Buf.CopyBytes(Offset, Self.Data, Self.DataOffset, Count)

		Self._Position = NewPosition

		SetLengthSafe(NewPosition)

		Return Count
	End

	' This transfers a raw segment of the internal buffer into 'S'. (Use at your own risk)
	Method TransferSegment:Void(S:Stream, Bytes:Int, Offset:Int)
		S.WriteAll(Data, Offset, Bytes)

		Return
	End

	' This may be used to transfer the internal data of this stream to another.
	Method TransferTo:Void(S:Stream, Offset:Int=0)
		Local ReadOffset:= (Self.Offset+Offset)

		TransferSegment(S, Length, ReadOffset)

		Return
	End

	' This may be used to transfer what has already been read.
	Method TransferPastData:Void(S:Stream, Offset:Int=0)
		Local ReadOffset:= (Self.Offset+Offset)

		TransferSegment(S, (Position-ReadOffset), ReadOffset)

		Return
	End

	' This may be used to read the number of bytes specified, from this stream then transfer it to another.
	Method TransferAmount:Void(S:Stream, Bytes:Int, Offset:Int=0)
		Local P:= S.Position

		TransferSegment(S, Bytes, (Self.Offset+Self.Position+Offset))

		S.Seek(P)

		Return
	End

	Method AutoResize:Bool(MinBytes:Int=0)
		If (Not OwnsBuffer) Then
			Return False
		Endif

		Return Resize(Max(Int(Float(Data.Length) * ResizeScalar), MinBytes))
	End

	Method SmartResize:Bool(MinBytes:Int)
		If (MinBytes <= DataLength) Then
			Return True
		Endif

		Return Resize(MinBytes)
	End

	Method Resize:Bool(NewSize:Int, Force:Bool=False)
		If (Not OwnsBuffer) Then
			Return False
		Endif

		If (SizeLimit <> NOLIMIT) Then
			If (NewSize > SizeLimit) Then
				If (Force) Then
					NewSize = Min(NewSize, SizeLimit)
				Else
					Return False
				Endif
			Endif
		Endif

		Data = ResizeBuffer(Data, NewSize, True, True, True)

		Return (Data <> Null)
	End

	Method SetLength:Void(Value:Int)
		Self._Length = Min(Value, DataLength)

		Return
	End

	Method SetLengthSafe:Void(Value:Int)
		SetLength(Max(Value, Self._Length))

		Return
	End

	' This sets the internal length to the current position.
	Method ClampToPosition:Void()
		SetLength(Position)

		Return
	End

	Method ResetLength:Void()
		SetLength(0)

		Return
	End

	' Call-backs:
	Method OnLoadDataComplete:Void(Data:DataBuffer, Path:String)
		If (Self.Data = Null) Then
			Self.Data = Data
			Self._Length = Data.Length
		Endif

		Return
	End

	' Properties (Public):

	' The internal I/O buffer.
	Method Data:DataBuffer() Property
		Return Self._Data
	End

	Method Eof:Int() Property
		Return (Position >= Length)
	End

	' The furthest this stream has written.
	Method Length:Int() Property
		Return Self._Length
	End

	' The number of bytes into the buffer this stream is.
	Method Position:Int() Property
		Return Self._Position
	End

	' The overall length of the 'Data' buffer. (Taking 'Offset' into account)
	Method RawSize:Int() Property
		Return Self._RawSize
	End

	' This will return the 'RawSize' property adjusted to the internal offset.
	Method DataLength:Int() Property
		Return (RawSize - Offset)
	End

	' The real position in the 'Data' buffer. (After 'Offset')
	Method DataOffset:Int() Property
		Return (Offset+Position)
	End

	' The number of bytes left in 'Data'. (Input only)
	Method BytesLeft:Int() Property
		Return Max(Length - Position, 0)
	End

	' This specifies the number of bytes left.
	' (Based on 'RawSize' and 'DataOffset'; use at your own risk)
	Method RealBytesLeft:Int() Property
		Return Max(RawSize - DataOffset, 0)
	End

	' This may be used for asynchronous buffer-loading.
	Method DataReady:Bool() Property
		Return (Data <> Null)
	End

	' Properties (Protected):
	Protected

	Method Data:Void(Input:DataBuffer) Property
		Self._Data = Input

		Return
	End

	Method RawSize:Void(Input:Int) Property
		Self._RawSize = Input

		Return
	End

	Public

	' Fields (Public):

	' Please use any corresponding properties when possible:

	' This acts as an internal offset inside the 'Data' buffer.
	Field Offset:Int

	' The (Local) position of the stream.
	Field _Position:Int

	' The furthest point we have written in the internal buffer.
	Field _Length:Int

	' The internal buffer's size-limit. (Used when resizing)
	Field SizeLimit:Int = NOLIMIT

	' A floating-point scalar used when a resize-operation occurs.
	' Numbers will be rounded as the hardware sees fit. (Usually rounds down)
	Field ResizeScalar:Float = Default_ResizeScalar

	' Booleans / Flags:

	' This may be used to toggle resizing the internal buffer.
	Field ShouldResize:Bool

	' This specifies if big-endian byte-order should be used.
	Field BigEndianStorage:Bool

	' This specifies if this stream owns the internal buffer.
	Field OwnsBuffer:Bool

	' Fields (Protected):
	Protected

	Field _Data:DataBuffer
	Field _RawSize:Int

	Public
End

Strict

Public

' Preprocessor related:
' Nothing so far.

' Imports (Public):
Import regal.byteorder

' BRL:
Import brl.stream

' Imports (Private):
Private

Import regal.util

' BRL:
Import brl.databuffer

Public

' Classes:
Class PublicDataStream Extends Stream Implements IOnLoadDataComplete
	' Constant variable(s):
	Const NOLIMIT:Int = 0 ' -1

	' Defaults:
	Const Default_ResizeScalar:Float = 1.5 ' 2.0

	' Booleans / Flags:
	Const Default_BigEndianStorage:Bool = False

	' Constructor(s) (Public):
	Method New(Size:Int, BigEndianStorage:Bool=Default_BigEndianStorage, Resizable:Bool=True, SizeLimit:Int=NOLIMIT)
		GenerateBuffer(Size)

		Self.BigEndianStorage = BigEndianStorage
		Self.ShouldResize = Resizable
		Self.SizeLimit = SizeLimit
	End

	Method New(B:DataBuffer, Length:Int, Offset:Int=0, Copy:Bool=False, BigEndianStorage:Bool=Default_BigEndianStorage, Resizable:Bool=True, SizeLimit:Int=NOLIMIT)
		If (Copy) Then
			GenerateBuffer(B)

			OwnsBuffer = True
		Else
			Self.Data = B
		Endif

		Self.Offset = Offset
		Self._Length = Length

		Self.RawSize = Length

		Self.BigEndianStorage = BigEndianStorage
		Self.ShouldResize = Resizable
		Self.SizeLimit = SizeLimit
	End

	Method New(Path:String, BigEndianStorage:Bool=Default_BigEndianStorage, Async:Bool=False)
		If (Not Async) Then
			OnLoadDataComplete(DataBuffer.Load(Path), Path)
		Else
			DataBuffer.LoadAsync(Path, Self)
		Endif

		Self.BigEndianStorage = BigEndianStorage
	End

	' Constructor(s) (Protected):
	Protected

	Method GenerateBuffer:Void(Size:Int)
		Self.Data = New DataBuffer(Size)
		Self.RawSize = Size
		Self.OwnsBuffer = True

		Return
	End

	' This will copy the contents of 'B' into an internally managed buffer.
	Method GenerateBuffer:Void(B:DataBuffer)
		GenerateBuffer(B.Length)

		B.CopyBytes(0, Data, 0, Data.Length)

		Return
	End

	Public

	' Destructor(s) (Protected):
	Protected

	Method DestructDetails:Void()
		Self.Offset = 0
		Self._Position = 0

		Return
	End

	Public

	' Destructor(s) (Public):
	Method FreeBuffer:Void(DiscardBuffer:Bool=True)
		If (Data <> Null) Then
			If (DiscardBuffer) Then
				Data.Discard()
			Endif

			Data = Null
		Endif

		OwnsBuffer = False

		RawSize = 0

		Return
	End

	Method Close:Void()
		FreeBuffer(OwnsBuffer)
		DestructDetails()

		Return
	End

	' The resulting 'DataBuffer' represents the 'Data'
	' property before destruction takes place.
	Method CloseWithoutBuffer:DataBuffer()
		Local OutputBuffer:= Data

		FreeBuffer(False)
		DestructDetails()

		Return OutputBuffer
	End

	' Methods:
	Method Seek:Int(Input:Int=0)
		Self._Position = Clamp(Input, 0, DataLength)

		Return Self._Position
	End

	Method Reset:Void()
		Seek()
		ResetLength()

		Return
	End

	' This states if the number of bytes specified may be read safely.
	Method WillOverReach:Bool(Bytes:Int)
		Return ReadWillOverReach(Bytes)
	End

	Method ReadWillOverReach:Bool(Bytes:Int)
		Return (Position+Bytes > Length)
	End

	Method WriteWillOverReach:Bool(Bytes:Int)
		Return (Position+Bytes > DataLength) ' Length
	End

	Method ReadShort:Int()
		If (BigEndianStorage) Then
			Return NToHS(Super.ReadShort())
		Endif

		Return Super.ReadShort()
	End

	Method ReadInt:Int()
		If (BigEndianStorage) Then
			Return NToHL(Super.ReadInt())
		Endif

		Return Super.ReadInt()
	End

	Method ReadFloat:Float()
		If (BigEndianStorage) Then
			Return NToHF(Super.ReadInt())
		Endif

		Return Super.ReadFloat()
	End

	Method WriteShort:Void(Value:Int)
		If (BigEndianStorage) Then
			Super.WriteShort(HToNS(Value))
		Else
			Super.WriteShort(Value)
		Endif

		Return
	End

	Method WriteInt:Void(Value:Int)
		If (BigEndianStorage) Then
			Super.WriteInt(HToNL(Value))
		Else
			Super.WriteInt(Value)
		Endif

		Return
	End

	Method WriteFloat:Void(Value:Float)
		If (BigEndianStorage) Then
			Super.WriteInt(HToNF(Value))
		Else
			Super.WriteFloat(Value)
		Endif

		Return
	End

	Method Read:Int(Buf:DataBuffer, Offset:Int, Count:Int)
		If (WillOverReach(Count)) Then
			Count = Self.BytesLeft
		Endif

		#Rem
			For Local Index:= 0 Until Count
				Buf.PokeByte(Offset+Index, Self.Data.PeekByte(Offset+Position+Index))
			Next
		#End

		Self.Data.CopyBytes(Self.DataOffset, Buf, Offset, Count)

		Self._Position += Count

		Return Count
	End

	Method Write:Int(Buf:DataBuffer, Offset:Int, Count:Int)
		Local NewPosition:= (Count+Position)

		If (NewPosition > DataLength) Then
			If (ShouldResize) Then
				AutoResize(Count)
			Else
				Count = (DataLength-Position) ' Max(..., 0)
			Endif
		Endif

		Buf.CopyBytes(Offset, Self.Data, Self.DataOffset, Count)

		Self._Position = NewPosition

		SetLengthSafe(NewPosition)

		Return Count
	End

	' This transfers a raw segment of the internal buffer into 'S'. (Use at your own risk)
	Method TransferSegment:Void(S:Stream, Bytes:Int, Offset:Int)
		S.WriteAll(Data, Offset, Bytes)

		Return
	End

	' This may be used to transfer the internal data of this stream to another.
	Method TransferTo:Void(S:Stream, Offset:Int=0)
		Local ReadOffset:= (Self.Offset+Offset)

		TransferSegment(S, Length, ReadOffset)

		Return
	End

	' This may be used to transfer what has already been read.
	Method TransferPastData:Void(S:Stream, Offset:Int=0)
		Local ReadOffset:= (Self.Offset+Offset)

		TransferSegment(S, (Position-ReadOffset), ReadOffset)

		Return
	End

	' This may be used to read the number of bytes specified, from this stream then transfer it to another.
	Method TransferAmount:Void(S:Stream, Bytes:Int, Offset:Int=0)
		Local P:= S.Position

		TransferSegment(S, Bytes, (Self.Offset+Self.Position+Offset))

		S.Seek(P)

		Return
	End

	Method AutoResize:Bool(MinBytes:Int=0)
		If (Not OwnsBuffer) Then
			Return False
		Endif

		Return Resize(Max(Int(Float(Data.Length) * ResizeScalar), MinBytes))
	End

	Method SmartResize:Bool(MinBytes:Int)
		If (MinBytes <= DataLength) Then
			Return True
		Endif

		Return Resize(MinBytes)
	End

	Method Resize:Bool(NewSize:Int, Force:Bool=False)
		If (Not OwnsBuffer) Then
			Return False
		Endif

		If (SizeLimit <> NOLIMIT) Then
			If (NewSize > SizeLimit) Then
				If (Force) Then
					NewSize = Min(NewSize, SizeLimit)
				Else
					Return False
				Endif
			Endif
		Endif

		Data = ResizeBuffer(Data, NewSize, True, True, True)

		Return (Data <> Null)
	End

	Method SetLength:Void(Value:Int)
		Self._Length = Min(Value, DataLength)

		Return
	End

	Method SetLengthSafe:Void(Value:Int)
		SetLength(Max(Value, Self._Length))

		Return
	End

	' This sets the internal length to the current position.
	Method ClampToPosition:Void()
		SetLength(Position)

		Return
	End

	Method ResetLength:Void()
		SetLength(0)

		Return
	End

	' Call-backs:
	Method OnLoadDataComplete:Void(Data:DataBuffer, Path:String)
		If (Self.Data = Null) Then
			Self.Data = Data
			Self._Length = Data.Length
		Endif

		Return
	End

	' Properties (Public):

	' The internal I/O buffer.
	Method Data:DataBuffer() Property
		Return Self._Data
	End

	Method Eof:Int() Property
		Return (Position >= Length)
	End

	' The furthest this stream has written.
	Method Length:Int() Property
		Return Self._Length
	End

	' The number of bytes into the buffer this stream is.
	Method Position:Int() Property
		Return Self._Position
	End

	' The overall length of the 'Data' buffer. (Taking 'Offset' into account)
	Method RawSize:Int() Property
		Return Self._RawSize
	End

	' This will return the 'RawSize' property adjusted to the internal offset.
	Method DataLength:Int() Property
		Return (RawSize - Offset)
	End

	' The real position in the 'Data' buffer. (After 'Offset')
	Method DataOffset:Int() Property
		Return (Offset+Position)
	End

	' The number of bytes left in 'Data'. (Input only)
	Method BytesLeft:Int() Property
		Return Max(Length - Position, 0)
	End

	' This specifies the number of bytes left.
	' (Based on 'RawSize' and 'DataOffset'; use at your own risk)
	Method RealBytesLeft:Int() Property
		Return Max(RawSize - DataOffset, 0)
	End

	' This may be used for asynchronous buffer-loading.
	Method DataReady:Bool() Property
		Return (Data <> Null)
	End

	' Properties (Protected):
	Protected

	Method Data:Void(Input:DataBuffer) Property
		Self._Data = Input

		Return
	End

	Method RawSize:Void(Input:Int) Property
		Self._RawSize = Input

		Return
	End

	Public

	' Fields (Public):

	' Please use any corresponding properties when possible:

	' This acts as an internal offset inside the 'Data' buffer.
	Field Offset:Int

	' The (Local) position of the stream.
	Field _Position:Int

	' The furthest point we have written in the internal buffer.
	Field _Length:Int

	' The internal buffer's size-limit. (Used when resizing)
	Field SizeLimit:Int = NOLIMIT

	' A floating-point scalar used when a resize-operation occurs.
	' Numbers will be rounded as the hardware sees fit. (Usually rounds down)
	Field ResizeScalar:Float = Default_ResizeScalar

	' Booleans / Flags:

	' This may be used to toggle resizing the internal buffer.
	Field ShouldResize:Bool

	' This specifies if big-endian byte-order should be used.
	Field BigEndianStorage:Bool

	' This specifies if this stream owns the internal buffer.
	Field OwnsBuffer:Bool

	' Fields (Protected):
	Protected

	Field _Data:DataBuffer
	Field _RawSize:Int

	Public
End


Changed example to just use diddy.collections

--HG--
extra : convert_revision : svn%3Ae934d3cd-0d57-f34a-94d0-9e8d31ae1c29/trunk%4055

Strict

Import diddy.collections
Import monkey.list

Function Main:Int()
	TestArrayList()
	'TestArrayListSorting()
	'SpeedComparison()
	'ArrayListSortingSpeed(10000)
	Return 0
	Return 0
End

Function TestArrayList:Void()
	Print("Adding 3 elements to l1...")
	Local l1:ArrayList<StringObject> = New ArrayList<StringObject>
	l1.Add("test3")
	l1.Add("test2")
	l1.Add("test1")
	Print("l1.Size="+l1.Size)

	Print("Adding 3 elements to l2...")
	Local l2:ArrayList<StringObject> = New ArrayList<StringObject>
	l2.Add("test6")
	l2.Add("test5")
	l2.Add("test4")
	Print("l2.Size="+l2.Size)

	Print("Adding contents of l2 to l1..")
	l1.AddAll(l2)
	Print("l1.Size="+l1.Size)

	Print("Calling l1.ToArray (creates a new array)...")
	Local arr:Object[] = l1.ToArray()
	Print("Array length="+arr.Length)
	Print("Looping on array with EachIn...")
	For Local o:Object = EachIn arr
		Print("value="+StringObject(o).value)
	Next

	Print("Calling l2.FillArray (reuses an existing array, and we know that the results will fit)...")
	Local len:Int = l2.FillArray(arr)
	Print("Returned value (size)="+len)
	Print("Looping on array with index...")
	For Local i:Int = 0 Until len
		Print("value["+i+"]="+StringObject(arr[i]).value)
	Next

	Print("Looping on l1 using EachIn...")
	For Local so:StringObject = EachIn l1
		Print("value="+so.value)
	Next

	Print("Looping on l1 manually with AbstractEnumerator...")
	Local oe:AbstractEnumerator<StringObject> = l1.ObjectEnumerator()
	While oe.HasNext()
		Local val:StringObject = oe.NextObject()
		Print("value="+val.value)
	End

	Print("Jumping to end of AbstractEnumerator and looping in reverse...")
	oe = l1.ObjectEnumerator()
	oe.Last()
	While oe.HasPrevious()
		Local val:StringObject = oe.PreviousObject()
		Print("value="+val.value)
	End
	Print("And now looping forward again...")
	While oe.HasNext()
		Local val:StringObject = oe.NextObject()
		Print("value="+val.value)
	End

	Print("Testing removing items with AbstractEnumerator (forward)...")
	Print("We'll remove the item 'test1'")
	oe = l1.ObjectEnumerator()
	While oe.HasNext()
		Local val:StringObject = oe.NextObject()
		Print("value="+val.value)
		If val.value = "test1" Then
			Print("Removing...")
			oe.Remove()
		End
	End

	Print("Now we'll loop back to make sure it worked...")
	oe.Last()
	While oe.HasPrevious()
		Local val:StringObject = oe.PreviousObject()
		Print("value="+val.value)
	End

	Print("Testing concurrency checks by removing within an EachIn loop (uncomment to test)...")
	#Rem
	For Local so:StringObject = EachIn l1
		Print("value="+so.value)
		Print("Manually removing it...")
		l1.RemoveAt(0)
		Print("Next call to HasNext should fail.")
	End
	#End
End

Function TestArrayListSorting:Void()
	Local al:ArrayList<IntObject> = New ArrayList<IntObject>()
	Print("Adding 8 ints")
	al.Add(New IntObject(5))
	al.Add(New IntObject(7))
	al.Add(New IntObject(5))
	al.Add(New IntObject(2))
	al.Add(New IntObject(10))
	al.Add(New IntObject(1))
	al.Add(New IntObject(11))
	al.Add(New IntObject(8))

	Print("Printing them pre-sort")
	For Local o:IntObject = EachIn(al)
		Print("o="+o.value)
	Next

	Print("Sorting ascending")
	al.Sort()
	Print("Printing them post-sort")
	For Local o:IntObject = EachIn(al)
		Print("o="+o.value)
	Next

	Print("Sorting Descending")
	al.Sort(True)
	Print("Printing them post-sort")
	For Local o:IntObject = EachIn(al)
		Print("o="+o.value)
	Next
End

Function SpeedComparison:Void()
	Local al:ArrayList<StringObject> = New ArrayList<StringObject>(1000000)
	Local lst:List<StringObject> = New List<StringObject>()

	Local startTime:Int

	Print("Testing adding 1000000 items to an ArrayList")
	startTime = RealMillisecs()
	For Local i:Int = 0 Until 1000000
		al.Add("test")'+i)
	Next
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing adding 1000000 items to a List")
	startTime = RealMillisecs()
	For Local i:Int = 0 Until 1000000
		lst.AddLast("test")'+i)
	Next
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing looping through an ArrayList with EachIn")
	startTime = RealMillisecs()
	For Local so:StringObject = EachIn al
		' do nothing
	Next
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing looping through a List with EachIn")
	startTime = RealMillisecs()
	For Local so:StringObject = EachIn lst
		' do nothing
	Next
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing looping through an ArrayList manually")
	startTime = RealMillisecs()
	For Local i:Int = 0 Until al.Size
		Local so:StringObject = al.Get(i)
		' do nothing
	Next
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing ToArray")
	startTime = RealMillisecs()
	Local arr:Object[] = al.ToArray()
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing looping through an ArrayList ToArray ("+arr.Length+" elements)")
	startTime = RealMillisecs()
	For Local i:Int = 0 Until arr.Length
		' do nothing
	Next
	Print("Took: "+(RealMillisecs()-startTime))
End

Function ArrayListSortingSpeed:Void(numToTest:Int=1000)
	Local startTime:Int

	Local al:ArrayList<IntObject> = New ArrayList<IntObject>(numToTest+10)
	Print("Seeding with: " + RealMillisecs())
	Print("Adding "+numToTest+" random ints")
	startTime = RealMillisecs()
	For Local i%=1 To numToTest
		al.Add(New IntObject(Int(Rnd()*1000)))
	Next
	Print("Took: "+(RealMillisecs()-startTime))
	Print("Size: "+al.Size)
	Print("First 10:")
	For Local i%=0 To 9
		Print(al.Get(i).value)
	Next

	Print("Sorting ascending")
	startTime = RealMillisecs()
	al.Sort()
	Print("Took: "+(RealMillisecs()-startTime))
	Print("First 10:")
	For Local i%=0 To 9
		Print(al.Get(i).value)
	Next

	Print("Clearing")
	startTime = RealMillisecs()
	al.Clear()
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Adding "+numToTest+" random ints again")
	startTime = RealMillisecs()
	For Local i%=1 To numToTest
		al.Add(New IntObject(Int(Rnd()*1000)))
	Next
	Print("Took: "+(RealMillisecs()-startTime))
	Print("Size: "+al.Size)
	Print("First 10:")
	For Local i%=0 To 9
		Print(al.Get(i).value)
	Next

	Print("Sorting descending")
	startTime = RealMillisecs()
	al.Sort(True)
	Print("Took: "+(RealMillisecs()-startTime))
	Print("First 10:")
	For Local i%=0 To 9
		Print(al.Get(i).value)
	Next

	Print("Trying floats")
	Local fal:ArrayList<FloatObject> = New ArrayList<FloatObject>(numToTest+10)
	Print("Adding "+numToTest+" random floats")
	startTime = RealMillisecs()
	For Local i%=1 To numToTest
		fal.Add(New FloatObject(Rnd()*1000))
	Next
	Print("Took: "+(RealMillisecs()-startTime))
	Print("Size: "+fal.Size)
	Print("First 10:")
	For Local i%=0 To 9
		Print(fal.Get(i).value)
	Next

	Print("Sorting ascending")
	startTime = RealMillisecs()
	fal.Sort()
	Print("Took: "+(RealMillisecs()-startTime))
	Print("First 10:")
	For Local i%=0 To 9
		Print(fal.Get(i).value)
	Next

	Print("Clearing")
	startTime = RealMillisecs()
	fal.Clear()
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Adding "+numToTest+" random floats again")
	startTime = RealMillisecs()
	For Local i%=1 To numToTest
		fal.Add(New FloatObject(Rnd()*1000))
	Next
	Print("Took: "+(RealMillisecs()-startTime))
	Print("Size: "+al.Size)
	Print("First 10:")
	For Local i%=0 To 9
		Print(fal.Get(i).value)
	Next

	Print("Sorting descending")
	startTime = RealMillisecs()
	fal.Sort(True)
	Print("Took: "+(RealMillisecs()-startTime))
	Print("First 10:")
	For Local i%=0 To 9
		Print(fal.Get(i).value)
	Next
End

Strict

Import diddy
Import monkey.list

Function Main:Int()
	game = New MyGame()
	Return 0
End

Class MyGame Extends DiddyApp
	Method OnCreate:Int()
		TestArrayList()
		'TestArrayListSorting()
		'SpeedComparison()
		'ArrayListSortingSpeed(10000)
		Return 0
	End
End

Function TestArrayList:Void()
	Print("Adding 3 elements to l1...")
	Local l1:ArrayList<StringObject> = New ArrayList<StringObject>
	l1.Add("test3")
	l1.Add("test2")
	l1.Add("test1")
	Print("l1.Size="+l1.Size)

	Print("Adding 3 elements to l2...")
	Local l2:ArrayList<StringObject> = New ArrayList<StringObject>
	l2.Add("test6")
	l2.Add("test5")
	l2.Add("test4")
	Print("l2.Size="+l2.Size)

	Print("Adding contents of l2 to l1..")
	l1.AddAll(l2)
	Print("l1.Size="+l1.Size)

	Print("Calling l1.ToArray (creates a new array)...")
	Local arr:Object[] = l1.ToArray()
	Print("Array length="+arr.Length)
	Print("Looping on array with EachIn...")
	For Local o:Object = EachIn arr
		Print("value="+StringObject(o).value)
	Next

	Print("Calling l2.FillArray (reuses an existing array, and we know that the results will fit)...")
	Local len:Int = l2.FillArray(arr)
	Print("Returned value (size)="+len)
	Print("Looping on array with index...")
	For Local i:Int = 0 Until len
		Print("value["+i+"]="+StringObject(arr[i]).value)
	Next

	Print("Looping on l1 using EachIn...")
	For Local so:StringObject = EachIn l1
		Print("value="+so.value)
	Next

	Print("Looping on l1 manually with AbstractEnumerator...")
	Local oe:AbstractEnumerator<StringObject> = l1.ObjectEnumerator()
	While oe.HasNext()
		Local val:StringObject = oe.NextObject()
		Print("value="+val.value)
	End

	Print("Jumping to end of AbstractEnumerator and looping in reverse...")
	oe = l1.ObjectEnumerator()
	oe.Last()
	While oe.HasPrevious()
		Local val:StringObject = oe.PreviousObject()
		Print("value="+val.value)
	End
	Print("And now looping forward again...")
	While oe.HasNext()
		Local val:StringObject = oe.NextObject()
		Print("value="+val.value)
	End

	Print("Testing removing items with AbstractEnumerator (forward)...")
	Print("We'll remove the item 'test1'")
	oe = l1.ObjectEnumerator()
	While oe.HasNext()
		Local val:StringObject = oe.NextObject()
		Print("value="+val.value)
		If val.value = "test1" Then
			Print("Removing...")
			oe.Remove()
		End
	End

	Print("Now we'll loop back to make sure it worked...")
	oe.Last()
	While oe.HasPrevious()
		Local val:StringObject = oe.PreviousObject()
		Print("value="+val.value)
	End

	Print("Testing concurrency checks by removing within an EachIn loop (uncomment to test)...")
	#Rem
	For Local so:StringObject = EachIn l1
		Print("value="+so.value)
		Print("Manually removing it...")
		l1.RemoveAt(0)
		Print("Next call to HasNext should fail.")
	End
	#End
End

Function TestArrayListSorting:Void()
	Local al:ArrayList<IntObject> = New ArrayList<IntObject>()
	Print("Adding 8 ints")
	al.Add(New IntObject(5))
	al.Add(New IntObject(7))
	al.Add(New IntObject(5))
	al.Add(New IntObject(2))
	al.Add(New IntObject(10))
	al.Add(New IntObject(1))
	al.Add(New IntObject(11))
	al.Add(New IntObject(8))

	Print("Printing them pre-sort")
	For Local o:IntObject = EachIn(al)
		Print("o="+o.value)
	Next

	Print("Sorting ascending")
	al.Sort()
	Print("Printing them post-sort")
	For Local o:IntObject = EachIn(al)
		Print("o="+o.value)
	Next

	Print("Sorting Descending")
	al.Sort(True)
	Print("Printing them post-sort")
	For Local o:IntObject = EachIn(al)
		Print("o="+o.value)
	Next
End

Function SpeedComparison:Void()
	Local al:ArrayList<StringObject> = New ArrayList<StringObject>(1000000)
	Local lst:List<StringObject> = New List<StringObject>()

	Local startTime:Int

	Print("Testing adding 1000000 items to an ArrayList")
	startTime = RealMillisecs()
	For Local i:Int = 0 Until 1000000
		al.Add("test")'+i)
	Next
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing adding 1000000 items to a List")
	startTime = RealMillisecs()
	For Local i:Int = 0 Until 1000000
		lst.AddLast("test")'+i)
	Next
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing looping through an ArrayList with EachIn")
	startTime = RealMillisecs()
	For Local so:StringObject = EachIn al
		' do nothing
	Next
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing looping through a List with EachIn")
	startTime = RealMillisecs()
	For Local so:StringObject = EachIn lst
		' do nothing
	Next
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing looping through an ArrayList manually")
	startTime = RealMillisecs()
	For Local i:Int = 0 Until al.Size
		Local so:StringObject = al.Get(i)
		' do nothing
	Next
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing ToArray")
	startTime = RealMillisecs()
	Local arr:Object[] = al.ToArray()
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Testing looping through an ArrayList ToArray ("+arr.Length+" elements)")
	startTime = RealMillisecs()
	For Local i:Int = 0 Until arr.Length
		' do nothing
	Next
	Print("Took: "+(RealMillisecs()-startTime))
End

Function ArrayListSortingSpeed:Void(numToTest:Int=1000)
	Local startTime:Int

	Local al:ArrayList<IntObject> = New ArrayList<IntObject>(numToTest+10)
	Print("Seeding with: " + RealMillisecs())
	Print("Adding "+numToTest+" random ints")
	startTime = RealMillisecs()
	For Local i%=1 To numToTest
		al.Add(New IntObject(Int(Rnd()*1000)))
	Next
	Print("Took: "+(RealMillisecs()-startTime))
	Print("Size: "+al.Size)
	Print("First 10:")
	For Local i%=0 To 9
		Print(al.Get(i).value)
	Next

	Print("Sorting ascending")
	startTime = RealMillisecs()
	al.Sort()
	Print("Took: "+(RealMillisecs()-startTime))
	Print("First 10:")
	For Local i%=0 To 9
		Print(al.Get(i).value)
	Next

	Print("Clearing")
	startTime = RealMillisecs()
	al.Clear()
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Adding "+numToTest+" random ints again")
	startTime = RealMillisecs()
	For Local i%=1 To numToTest
		al.Add(New IntObject(Int(Rnd()*1000)))
	Next
	Print("Took: "+(RealMillisecs()-startTime))
	Print("Size: "+al.Size)
	Print("First 10:")
	For Local i%=0 To 9
		Print(al.Get(i).value)
	Next

	Print("Sorting descending")
	startTime = RealMillisecs()
	al.Sort(True)
	Print("Took: "+(RealMillisecs()-startTime))
	Print("First 10:")
	For Local i%=0 To 9
		Print(al.Get(i).value)
	Next

	Print("Trying floats")
	Local fal:ArrayList<FloatObject> = New ArrayList<FloatObject>(numToTest+10)
	Print("Adding "+numToTest+" random floats")
	startTime = RealMillisecs()
	For Local i%=1 To numToTest
		fal.Add(New FloatObject(Rnd()*1000))
	Next
	Print("Took: "+(RealMillisecs()-startTime))
	Print("Size: "+fal.Size)
	Print("First 10:")
	For Local i%=0 To 9
		Print(fal.Get(i).value)
	Next

	Print("Sorting ascending")
	startTime = RealMillisecs()
	fal.Sort()
	Print("Took: "+(RealMillisecs()-startTime))
	Print("First 10:")
	For Local i%=0 To 9
		Print(fal.Get(i).value)
	Next

	Print("Clearing")
	startTime = RealMillisecs()
	fal.Clear()
	Print("Took: "+(RealMillisecs()-startTime))

	Print("Adding "+numToTest+" random floats again")
	startTime = RealMillisecs()
	For Local i%=1 To numToTest
		fal.Add(New FloatObject(Rnd()*1000))
	Next
	Print("Took: "+(RealMillisecs()-startTime))
	Print("Size: "+al.Size)
	Print("First 10:")
	For Local i%=0 To 9
		Print(fal.Get(i).value)
	Next

	Print("Sorting descending")
	startTime = RealMillisecs()
	fal.Sort(True)
	Print("Took: "+(RealMillisecs()-startTime))
	Print("First 10:")
	For Local i%=0 To 9
		Print(fal.Get(i).value)
	Next
End


Removed config from main diddy import.

Removed config from main diddy import.

'
' Module diddy.diddy
'

Import functions
Import framework
Import inputcache
Import tween
Import collections
Import assert
Import gui
Import serialization
Import xml
Import io

'
' Module diddy.diddy
'

Import functions
Import framework
Import inputcache
Import tween
Import collections
Import assert
Import gui
Import config
Import serialization
Import xml
Import io


add Scene.emitterAutoEnable with default true

Strict

Private

Import bono

Public

Class Scene Implements Sceneable, Updateable, Suspendable Abstract
    Private

    Field childs:List<Object> = New List<Object>
    Field keyEventsEnabled:Bool
    Field keyEmitter:KeyEmitter
    Field keyhandlerFan:KeyhandlerFan
    Field touchEventsEnabled:Bool
    Field touchEmitter:TouchEmitter
    Field touchableFan:TouchableFan

    Public

    Field emitterAutoEnable := True

    Method OnSceneEnter:Void()
    End

    Method OnSceneLeave:Void()
    End

    Method OnRender:Void()
        For Local obj:Object = EachIn childs
            If Renderable(obj) Then Renderable(obj).OnRender()
        End
    End

    Method OnUpdate:Void(timer:DeltaTimer)
        For Local obj:Object = EachIn childs
            If Updateable(obj) Then Updateable(obj).OnUpdate(timer)
        End
    End

    Method OnResume:Void()
        For Local obj:Object = EachIn childs
            If Suspendable(obj) Then Suspendable(obj).OnResume()
        End
    End

    Method OnSuspend:Void()
        For Local obj:Object = EachIn childs
            If Suspendable(obj) Then Suspendable(obj).OnSuspend()
        End
    End

    Method ClearChilds:Void()
        For Local child:Object = EachIn childs
            RemoveChild(child)
        End

        EnableTouchEvents(touchEventsEnabled)
        EnableKeyEvents(keyEventsEnabled)
    End

    Method AddChild:Void(child:Object)
        If Not childs.Contains(child) Then childs.AddLast(child)

        If Keyhandler(child)
            If Not keyEventsEnabled And emitterAutoEnable Then EnableKeyEvents()
            GetKeyhandlerFan().Add(Keyhandler(child))
        End
        If Touchable(child)
            If Not touchEventsEnabled And emitterAutoEnable Then EnableTouchEvents()
            GetTouchableFan().Add(Touchable(child))
        End
    End

    Method RemoveChild:Void(child:Object)
        childs.RemoveEach(child)

        If Keyhandler(child) Then GetKeyhandlerFan().Remove(Keyhandler(child))
        If Touchable(child) Then GetTouchableFan().Remove(Touchable(child))
    End

    Method GetDirector:Director()
        Return Director.Shared()
    End

    Method GetApp:App()
        Return GetDirector().GetApp()
    End

    Method EnableTouchEvents:Void(flag:Bool=True)
        If Not touchEmitter And Not flag Then Return

        If flag
            AddChild(GetTouchEmitter())
        Else
            RemoveChild(GetTouchEmitter())
        End
        touchEventsEnabled = flag
    End

    Method GetTouchEmitter:TouchEmitter()
        If Not touchEmitter
            touchEmitter = New TouchEmitter()
            touchEmitter.handler = GetTouchableFan()
        End

        Return touchEmitter
    End

    Method EnableKeyEvents:Void(flag:Bool=True)
        If Not keyEmitter And Not flag Then Return

        If flag
            AddChild(GetKeyEmitter())
        Else
            RemoveChild(GetKeyEmitter())
        End
        keyEventsEnabled = flag
    End

    Method GetKeyEmitter:KeyEmitter()
        If Not keyEmitter
            keyEmitter = New KeyEmitter()
            keyEmitter.handler = GetKeyhandlerFan()
        End

        Return keyEmitter
    End

    Private

    Method GetTouchableFan:TouchableFan()
        If Not touchableFan
            touchableFan = New TouchableFan()
            If Touchable(Self) Then touchableFan.Add(Touchable(Self))
        End

        Return touchableFan
    End

    Method GetKeyhandlerFan:KeyhandlerFan()
        If Not keyhandlerFan
            keyhandlerFan = New KeyhandlerFan()
            If Keyhandler(Self) Then keyhandlerFan.Add(Keyhandler(Self))
        End

        Return keyhandlerFan
    End
End

Strict

Private

Import bono

Public

Class Scene Implements Sceneable, Updateable, Suspendable Abstract
    Private

    Field childs:List<Object> = New List<Object>
    Field keyEventsEnabled:Bool
    Field keyEmitter:KeyEmitter
    Field keyhandlerFan:KeyhandlerFan
    Field touchEventsEnabled:Bool
    Field touchEmitter:TouchEmitter
    Field touchableFan:TouchableFan

    Public

    Method OnSceneEnter:Void()
    End

    Method OnSceneLeave:Void()
    End

    Method OnRender:Void()
        For Local obj:Object = EachIn childs
            If Renderable(obj) Then Renderable(obj).OnRender()
        End
    End

    Method OnUpdate:Void(timer:DeltaTimer)
        For Local obj:Object = EachIn childs
            If Updateable(obj) Then Updateable(obj).OnUpdate(timer)
        End
    End

    Method OnResume:Void()
        For Local obj:Object = EachIn childs
            If Suspendable(obj) Then Suspendable(obj).OnResume()
        End
    End

    Method OnSuspend:Void()
        For Local obj:Object = EachIn childs
            If Suspendable(obj) Then Suspendable(obj).OnSuspend()
        End
    End

    Method ClearChilds:Void()
        For Local child:Object = EachIn childs
            RemoveChild(child)
        End

        EnableTouchEvents(touchEventsEnabled)
        EnableKeyEvents(keyEventsEnabled)
    End

    Method AddChild:Void(child:Object)
        If Not childs.Contains(child) Then childs.AddLast(child)

        If keyhandlerFan And Keyhandler(child)
            GetKeyhandlerFan().Add(Keyhandler(child))
        End
        If touchableFan And Touchable(child)
            GetTouchableFan().Add(Touchable(child))
        End
    End

    Method RemoveChild:Void(child:Object)
        childs.RemoveEach(child)

        If keyhandlerFan And Keyhandler(child)
            GetKeyhandlerFan().Remove(Keyhandler(child))
        End
        If touchableFan And Touchable(child)
            GetTouchableFan().Remove(Touchable(child))
        End
    End

    Method GetDirector:Director()
        Return Director.Shared()
    End

    Method GetApp:App()
        Return GetDirector().GetApp()
    End

    Method EnableTouchEvents:Void(flag:Bool=True)
        If Not touchEmitter And Not flag Then Return

        If flag
            AddChild(GetTouchEmitter())
        Else
            RemoveChild(GetTouchEmitter())
        End
        touchEventsEnabled = flag
    End

    Method GetTouchEmitter:TouchEmitter()
        If Not touchEmitter
            touchEmitter = New TouchEmitter()
            touchEmitter.handler = GetTouchableFan()
        End

        Return touchEmitter
    End

    Method EnableKeyEvents:Void(flag:Bool=True)
        If Not keyEmitter And Not flag Then Return

        If flag
            AddChild(GetKeyEmitter())
        Else
            RemoveChild(GetKeyEmitter())
        End
        keyEventsEnabled = flag
    End

    Method GetKeyEmitter:KeyEmitter()
        If Not keyEmitter
            keyEmitter = New KeyEmitter()
            keyEmitter.handler = GetKeyhandlerFan()
        End

        Return keyEmitter
    End

    Private

    Method GetTouchableFan:TouchableFan()
        If Not touchableFan
            touchableFan = New TouchableFan()

            If Touchable(Self) Then touchableFan.Add(Touchable(Self))

            For Local obj:Object = EachIn childs
                If Not Touchable(obj) Then Continue
                touchableFan.Add(Touchable(obj))
            End
        End

        Return touchableFan
    End

    Method GetKeyhandlerFan:KeyhandlerFan()
        If Not keyhandlerFan
            keyhandlerFan = New KeyhandlerFan()

            If Keyhandler(Self) Then keyhandlerFan.Add(Keyhandler(Self))

            For Local obj:Object = EachIn childs
                If Not Keyhandler(obj) Then Continue
                keyhandlerFan.Add(Keyhandler(obj))
            End
        End

        Return keyhandlerFan
    End
End


Removed call to UseMonkeyCoords property.

Strict

Import mojo
Import diddy

Global testScreen:ParticleTestScreen

Function Main:Int()
	game = New ParticleTestApp
	Return 0
End

Class ParticleTestApp Extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()
		drawFPSOn = True
		testScreen = New ParticleTestScreen
		testScreen.PreStart()
		Return 0
	End
End

Class ParticleTestScreen Extends Screen
	Field ps:ParticleSystem
	Field pg:ParticleGroup
	Field e:Emitter
	Field e2:Emitter
	Field f:Force
	Field pf:PointForce
	Field speck:Image

	Field emitting:Bool = True
	Field emitCount:Int = 3
	Field rendering:Bool = True

	Method New()
		name = "Particle System Test"
		speck = LoadImage("speck.png",,Image.MidHandle)
		CreateSystem()
	End

	Method Start:Void()
		game.screenFade.Start(50, False)
	End

	Method Render:Void()
		Cls
		If rendering Then ps.Render()
		SetColor(255,255,255)
	End

	Method DebugRender:Void()
		SetColor(255,255,255)
		SetAlpha(1)
#If TARGET="android" Or TARGET="ios" Then
		DrawText("Tap top half to toggle emitter, bottom half to toggle rendering",0,20)
		DrawText("pg.AliveParticles="+pg.AliveParticles,0,35)
#Else
		DrawText("Space: toggle emitter, R: toggle rendering",0,20)
		DrawText("Hold Shift: Cursor repels, Hold Control: Cursor attracts",0,35)
		DrawText("Up/Down arrows increase/decrease emit count from 1-20. Current: "+emitCount,0,50)
		DrawText("pg.AliveParticles="+pg.AliveParticles,0,65)
#End
	End

	Method Update:Void()
		pf.X = game.mouseX
		pf.Y = SCREEN_HEIGHT-game.mouseY
		If KeyDown(KEY_CONTROL) Then
			pf.Acceleration = 300
			pf.Enabled = True
			f.Enabled = False
		Elseif KeyDown(KEY_SHIFT) Then
			pf.Acceleration = -300
			pf.Enabled = True
			f.Enabled = False
		Else
			pf.Enabled = False
			f.Enabled = True
		End

		If KeyHit(KEY_SPACE) Then emitting = Not emitting
		If KeyHit(KEY_R) Then rendering = Not rendering

		If TouchHit() Then
			If TouchY() < SCREEN_HEIGHT2 Then
				emitting = Not emitting
			Else
				rendering = Not rendering
			End
		End

		If KeyHit(KEY_UP) Then emitCount+=1
		If KeyHit(KEY_DOWN) Then emitCount-=1
		If emitCount < 1 Then emitCount = 1
		If emitCount > 20 Then emitCount = 20

		If emitting Then
			e.EmitAt(emitCount, game.mouseX, game.mouseY)
		End
		ps.Update(dt.frametime)
		If KeyHit(KEY_ESCAPE) Then
			game.screenFade.Start(50, True)
			game.nextScreen = game.exitScreen
		End
	End

	Method CreateSystem:Void()
		ps = New ParticleSystem()

		pg = New ParticleGroup(5000) ' group of 5000 particles
		ps.Groups.Add(pg)

		f = New ConstantForce(0,150) ' constant downward force of 150 pixels per second per second
		pg.Forces.Add(f)
		pf = New PointForce(0, 0, 0)
		pg.Forces.Add(pf)

		e = New Emitter
		e.SetParticleRGBInterpolated(255,0,0,0,255,0) ' fade from red to green
		e.Life = 3 ' lives for 3 seconds
		e.SetPolarVelocity(-PI/2, PI/3, 250, 50) ' points up with a spread of 60 degrees
		e.AlphaInterpolationTime = 0.5 ' will start fading out when there is half a second of life left
		e.ParticleImage = speck
		e.SetParticleScale(1.5,2)
		e.Group = pg

		e2 = New Emitter
		e2.Life = 3 ' lives for 3 seconds
		e2.AlphaInterpolationTime = 0.5 ' will start fading out when there is half a second of life left
		e2.SetPolarVelocity(0,2*PI,50,10) ' points right with a spread of 360 degrees (a full circle)
		e2.SetParticleRGBInterpolated(0,255,255,0,255,0) ' fade from cyan to green
		e2.SetParticleScale(2,2)
		e2.SetParticleRotation(0, PI/8, 0, PI/2)
		e.AddDeathEmitter(e2, 0.05) ' 5% chance to fire off this emitter when a particle from other emitter dies
	End
End

Strict

Import mojo
Import diddy

Global testScreen:ParticleTestScreen

Function Main:Int()
	game = New ParticleTestApp
	Return 0
End

Class ParticleTestApp Extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()
		drawFPSOn = True
		testScreen = New ParticleTestScreen
		testScreen.PreStart()
		Return 0
	End
End

Class ParticleTestScreen Extends Screen
	Field ps:ParticleSystem
	Field pg:ParticleGroup
	Field e:Emitter
	Field e2:Emitter
	Field f:Force
	Field pf:PointForce
	Field speck:Image

	Field emitting:Bool = True
	Field emitCount:Int = 3
	Field rendering:Bool = True

	Method New()
		name = "Particle System Test"
		speck = LoadImage("speck.png",,Image.MidHandle)
		CreateSystem()
	End

	Method Start:Void()
		game.screenFade.Start(50, False)
	End

	Method Render:Void()
		Cls
		If rendering Then ps.Render()
		SetColor(255,255,255)
	End

	Method DebugRender:Void()
		SetColor(255,255,255)
		SetAlpha(1)
#If TARGET="android" Or TARGET="ios" Then
		DrawText("Tap top half to toggle emitter, bottom half to toggle rendering",0,20)
		DrawText("pg.AliveParticles="+pg.AliveParticles,0,35)
#Else
		DrawText("Space: toggle emitter, R: toggle rendering",0,20)
		DrawText("Hold Shift: Cursor repels, Hold Control: Cursor attracts",0,35)
		DrawText("Up/Down arrows increase/decrease emit count from 1-20. Current: "+emitCount,0,50)
		DrawText("pg.AliveParticles="+pg.AliveParticles,0,65)
#End
	End

	Method Update:Void()
		pf.X = game.mouseX
		pf.Y = SCREEN_HEIGHT-game.mouseY
		If KeyDown(KEY_CONTROL) Then
			pf.Acceleration = 300
			pf.Enabled = True
			f.Enabled = False
		Elseif KeyDown(KEY_SHIFT) Then
			pf.Acceleration = -300
			pf.Enabled = True
			f.Enabled = False
		Else
			pf.Enabled = False
			f.Enabled = True
		End
		If KeyHit(KEY_M)
			pg.UseMonkeyCoords = Not pg.UseMonkeyCoords
		End

		If KeyHit(KEY_SPACE) Then emitting = Not emitting
		If KeyHit(KEY_R) Then rendering = Not rendering

		If TouchHit() Then
			If TouchY() < SCREEN_HEIGHT2 Then
				emitting = Not emitting
			Else
				rendering = Not rendering
			End
		End

		If KeyHit(KEY_UP) Then emitCount+=1
		If KeyHit(KEY_DOWN) Then emitCount-=1
		If emitCount < 1 Then emitCount = 1
		If emitCount > 20 Then emitCount = 20

		If emitting Then
			e.EmitAt(emitCount, game.mouseX, game.mouseY)
		End
		ps.Update(dt.frametime)
		If KeyHit(KEY_ESCAPE) Then
			game.screenFade.Start(50, True)
			game.nextScreen = game.exitScreen
		End
	End

	Method CreateSystem:Void()
		ps = New ParticleSystem()

		pg = New ParticleGroup(5000) ' group of 5000 particles
		ps.Groups.Add(pg)

		f = New ConstantForce(0,150) ' constant downward force of 150 pixels per second per second
		pg.Forces.Add(f)
		pf = New PointForce(0, 0, 0)
		pg.Forces.Add(pf)

		e = New Emitter
		e.SetParticleRGBInterpolated(255,0,0,0,255,0) ' fade from red to green
		e.Life = 3 ' lives for 3 seconds
		e.SetPolarVelocity(-PI/2, -PI/3, 250, 50) ' points up with a spread of 60 degrees
		e.AlphaInterpolationTime = 0.5 ' will start fading out when there is half a second of life left
		e.ParticleImage = speck
		e.SetParticleScale(1.5,2)
		e.Group = pg

		e2 = New Emitter
		e2.Life = 3 ' lives for 3 seconds
		e2.AlphaInterpolationTime = 0.5 ' will start fading out when there is half a second of life left
		e2.SetPolarVelocity(0,2*PI,50,10) ' points right with a spread of 360 degrees (a full circle)
		e2.SetParticleRGBInterpolated(0,255,255,0,255,0) ' fade from cyan to green
		e2.SetParticleScale(2,2)
		e2.SetParticleRotation(0, PI/8, 0, PI/2)
		e.AddDeathEmitter(e2, 0.05) ' 5% chance to fire off this emitter when a particle from other emitter dies
	End
End


add AssertScreenNotEquals to AppTestCase

Strict

Private

Import bono
Import mojo

Public

Class AppTestCase Extends TestCase
    Private

    Global imageRenderColor := New Color()

    Public

    Method SetUp:Void()
        Super.SetUp()
        Cls()
    End

    Method SwapAndSleep:Void()
        SwapBuffer()
        For Local i := 0 To 2000000000
            Millisecs()
        End
    End

    Method SwapBuffer:Void()
        GetGraphicsDevice().EndRender()
        mojo.graphics.EndRender()

        mojo.graphics.BeginRender()
        GetGraphicsDevice().BeginRender()
    End

    Method AssertScreenEquals:Void(imgfile:String)
        Local data := LoadPixelData(imgfile)
        If Not HasPixelDifference(data) Then Return

        Throw New AssertionFailedException(
            "Pixel difference found between the current " +
            "render buffer and " + imgfile)
    End

    Method AssertScreenNotEquals:Void(imgfile:String)
        Local data := LoadPixelData(imgfile)
        If HasPixelDifference(data) Then Return

        Throw New AssertionFailedException(
            "render buffer and " + imgfile + " are identical")
    End

    Private

    Method LoadPixelData:Int[][](imgfile:String)
        Local img := LoadImage(imgfile)
        AssertNotNull(img)

        Local p1 := New Int[img.Width() * img.Height()]
        ReadPixels(p1, 0, 0, img.Width(), img.Height())

        RenderImage(img)

        Local p2 := New Int[img.Width() * img.Height()]
        ReadPixels(p2, 0, 0, img.Width(), img.Height())

        Return [p1, p2]
    End

    Method RenderImage:Void(img:Image)
        ' It's important to ensure a clean state of the global color / alpha
        ' value AND a blank canvas!

        Cls()
        imageRenderColor.Activate()
        DrawImage(img, 0, 0)
        imageRenderColor.Deactivate()
    End

    Method HasPixelDifference:Bool(data:Int[][])
        For Local i := 0 Until data[0].Length()
            If Not (data[0][i] = data[1][i]) Then Return True
        End

        Return False
    End
End

Strict

Private

Import bono
Import mojo

Public

Class AppTestCase Extends TestCase
    Private

    Global imageRenderColor := New Color()

    Public

    Method SetUp:Void()
        Super.SetUp()
        Cls()
    End

    Method SwapAndSleep:Void()
        SwapBuffer()
        For Local i := 0 To 2000000000
            Millisecs()
        End
    End

    Method SwapBuffer:Void()
        GetGraphicsDevice().EndRender()
        mojo.graphics.EndRender()

        mojo.graphics.BeginRender()
        GetGraphicsDevice().BeginRender()
    End

    Method AssertScreenEquals:Void(imgfile:String)
        Local data := LoadPixelData(imgfile)
        If Not HasPixelDifference(data) Then Return

        Throw New AssertionFailedException(
            "Pixel difference found between the current " +
            "render buffer and" + imgfile)
    End

    Private

    Method LoadPixelData:Int[][](imgfile:String)
        Local img := LoadImage(imgfile)
        AssertNotNull(img)

        Local p1 := New Int[img.Width() * img.Height()]
        ReadPixels(p1, 0, 0, img.Width(), img.Height())

        RenderImage(img)

        Local p2 := New Int[img.Width() * img.Height()]
        ReadPixels(p2, 0, 0, img.Width(), img.Height())

        Return [p1, p2]
    End

    Method RenderImage:Void(img:Image)
        ' It's important to ensure a clean state of the global color / alpha
        ' value AND a blank canvas!

        Cls()
        imageRenderColor.Activate()
        DrawImage(img, 0, 0)
        imageRenderColor.Deactivate()
    End

    Method HasPixelDifference:Bool(data:Int[][])
        For Local i := 0 Until data[0].Length()
            If Not (data[0][i] = data[1][i]) Then Return True
        End

        Return False
    End
End


Moved the 'ResizeBuffer' command to 'publicdatastream'

Strict

Public

' Preprocessor related:
#Rem
#If TARGET = "glfw" Or TARGET = "sexy"
	#GLFW_TARGET = True
#End
#End

#If CONFIG = "debug"
	#DEBUG_PRINT = True
#Else
	#DEBUG_PRINT = False
#End

#DEBUG_PRINT_QUOTES = False

' Imports (Public):
Import byteorder
Import imagedimensions
Import retrostrings
Import stringutil
Import vector

#If CONSOLE_IMPLEMENTED
	Import console
#End

Import sizeof
Import time

#If Not BRL_GAMETARGET_IMPLEMENTED
	Import mojoemulator
#End

Import publicdatastream

' Imports (Private):
Private

' Official:

' BRL:
Import brl.databuffer
Import brl.stream
Import brl.filepath

' Unofficial:

' ImmutableOctet:
Import autostream

Public

' Constant variable(s):

' Used mainly for external code where '-9999...' isn't ever going to be valid.
' You should stick to 'Null' in most cases; try not to use this.
Const NOVAR:Int = -999999

Const ErrorTemplate:String = "[ERROR] {Debug}: " ' + Space
Const LogTemplate:String = "[Info] {Debug}: " ' + Space

' Global variable(s) (Public):
#If CONSOLE_IMPLEMENTED
	Global DebugConsole:Console = Null
#End

' Global variable(s) (Private):
Private

' Nothing so far.

Public

' Functions:
Function FLAG:Int(ID:Int)
	Return Pow(2, ID)
End

' This command is little-endian only:
Function AsByte:Int(I:Int)
	Return ((I Shl 24) Shr 24)
End

' This command treats data as 8-bit (Turns something like $F7, into $FFFFFFF7)
' This is only applied on little-endian systems,
' please use this command for hex values (Monkey converts hex on compile-time).
Function PaddedHex:Int(I:Int)
	If (Not BigEndian()) Then
		Return AsByte(I)
	Endif

	Return I
End

Function Transfer:Bool(InputStream:Stream, OutputStream:Stream, DataSize:Int)
	' Check for errors:
	If (DataSize = 0) Then Return False
	If (InputStream = Null Or InputStream.Eof()) Then Return False
	If (OutputStream = Null) Then Return False

	' Local variable(s):
	#If FLAG_TEMP_BUFFERS
		Local Data:DataBuffer = New DataBuffer(DataSize)
		Local DataLength:= DataSize ' Data.Length()

		' Read from the input-stream, then write to the output-stream:
		InputStream.Read(Data, 0, DataLength)
		OutputStream.Write(Data, 0, DataLength)

		Data.Discard()
	#Else
		'Local Data:Int[DataSize]

		For Local I:Int = 1 To DataSize ' Data.Length()
			OutputStream.WriteByte(InputStream.ReadByte())
		Next
	#End

	' Return the default response.
	Return True
End

Function Sq:Float(Input:Float)
	Return Pow(Input, 2.0) ' Input * Input
End

Function Sq:Int(Input:Int)
	Return Pow(Input, 2)
End

Function WrapAngle:Float(A:Float)
	While (A < 0.0)
		A += 360.0
	Wend

	While (A > 360.0)
		A -= 360.0
	Wend

	Return A
End

Function WrapColor:Float(C:Float)
	#Rem
	While (C < 0.0)
		C += 255.0
	Wend

	While (C > 255.0)
		C -= 255.0
	Wend
	#End

	Return C Mod 256.0
End

#If CONSOLE_IMPLEMENTED
	Function DebugBind:Bool(C:Console)
		If (C = Null) Then Return False

		DebugConsole = C

		' Return the default response.
		Return True
	End
#End

Function DebugError:Void(Msg:String, StopExecution:Bool=True)
	#If DEBUG_PRINT
		' Local variable(s):
		Local TempMsg:String

		If (StopExecution) Then
			TempMsg = ErrorTemplate
		Else
			TempMsg = LogTemplate
		Endif

		#If DEBUG_PRINT_QUOTES
			Msg = Tempmsg + Quote + Msg + Quote
		#Else
			Msg = TempMsg + Msg
		#End

		#If CONSOLE_IMPLEMENTED
			If (DebugConsole <> Null) Then
				DebugConsole.WriteLine(Msg, True)
			Else
		#End

		Print(Msg)

		#If CONSOLE_IMPLEMENTED
			Endif
		#End
	#Else
		' This may change later:
		If (StopExecution) Then
			Msg = ErrorTemplate + Msg
		Endif
	#End

	If (StopExecution) Then
		#If CONFIG = "debug"
			#If DEBUG_PRINT
				DebugStop()
			#Else
				Error(Msg)
			#End
		#Else
			#If DEBUG_PRINT
				Local FinalStr:String = (ErrorTemplate + Quote + "Attempt to stop execution failed. (Reason: Release-mode)" + Quote)

				#If CONSOLE_IMPLEMENTED
					If (DebugConsole <> Null) Then
						DebugConsole.WriteLine(FinalStr, True)
					Else
				#End

				Print(FinalStr)

				#If CONSOLE_IMPLEMENTED
					Endif
				#End
			#Else
				Error(Msg)
			#End
		#End
	Endif

	#If CONFIG = "debug"
		' Nothing so far.
	#Else
		' Nothing so far.
	#End

	Return
End

Function DebugPrint:Void(Str:String, StopExecution:Bool=False)
	DebugError(Str, StopExecution)

	Return
End

' Classes:
Class GenericUtilities<T>
	' Global variable(s):
	Global NIL:T

	' Functions:
	Function IndexOfList:T(L:List<T>, Index:Int=0)
		' Local variable(s):
		Local Response:T
		Local Data:list.Node<T> = L.FirstNode()

		For Local I:= 0 Until Index
			Data = Data.NextNode()

			' Not my favorite method, but it works:
			If (Data = Null) Then
				Return Response
			Endif
		Next

		Response = Data.Value()

		' Return the value of the assessed node.
		Return Response
	End

	Function CopyArray:T[](Source:T[], Destination:T[])
		Local MinLength:Int = Min(Source.Length(), Destination.Length())

		For Local Index:Int = 0 Until MinLength
			Destination[Index] = Source[Index]
		Next

		Return Destination
	End

	Function CloneArray:T[](Source:T[], Destination:T[]=[])
		If (Destination.Length() = 0) Then
			Destination = New T[Source.Length()]
		Endif

		Return CopyArray(Source, Destination)
	End

	Function Read:T(S:Stream)
		Return Read(S, NIL)
	End

	Function Read:T[](S:Stream, DataArray:T[], Size:Int=-1)
		If (Size = -1) Then Size = S.ReadInt()

		If (Size > 0) Then
			If (DataArray.Length() = 0) Then
				DataArray = New T[Size]
			Endif
		Endif

		' Local variable(s):
		Local DLength:Int = Min(DataArray.Length, Size)

		For Local Index:= 0 Until DLength
			DataArray[Index] = Read(S, NIL)
		Next

		Return DataArray
	End

	Function Write:Void(S:Stream, DataArray:T[], WriteLength:Bool=True)
		' Local variable(s):
		Local DLength:= DataArray.Length()

		If (WriteLength) Then S.WriteInt(DLength)

		For Local Index:= 0 Until DLength
			Write(S, DataArray[Index])
		Next

		Return
	End

	Function Read:Int(S:Stream, Indentifier:Int, Size:Int=4)
		' Local variable(s):
		Local Data:Int = 0

		If (Size > 1) Then
			If (Size < 4) Then
				Data = S.ReadShort()
			Else
				If (Size > 4) Then
					S.ReadInt()
				Endif

				Data = S.ReadInt()
			Endif
		Else
			Data = S.ReadByte()
		Endif

		Return Data
	End

	Function Write:Void(S:Stream, Data:Int, Size:Int=4)
		Size = IntSize(Size)

		If (Size > 1) Then
			If (Size < 4) Then
				S.WriteShort(Data)
			Else
				If (Size > 4) Then
					S.WriteInt(0)
				Endif

				S.WriteInt(Data)
			Endif
		Else
			S.WriteByte(Data)
		Endif

		Return
	End

	#Rem
	Function Read:Bool(S:Stream, Identifier:Bool)
		Return Read(S, Int(Identifier), 1)
	End

	Function Write:Void(S:Stream, B:Bool)
		Write(S, Int(B), 1)

		Return
	End
	#End

	Function Read:Float(S:Stream, Identifier:Float)
		Return S.ReadFloat()
	End

	Function Write:Void(S:Stream, Data:Float)
		S.WriteFloat(Data)

		Return
	End

	Function Read:String(S:Stream, Data:String, Encoding:String="utf8", Size:Int=-1)
		If (Size = -1) Then Size = S.ReadInt()

		Data = S.ReadString(Size, Encoding)

		' Return the newly read string.
		Return Data
	End

	Function Write:Void(S:Stream, Data:String, Encoding:String="utf8", WriteSize:Bool=True)
		If (WriteSize) Then S.WriteInt(Data.Length())
		S.WriteString(Data, Encoding)

		Return
	End
End

Strict

Public

' Preprocessor related:
#Rem
#If TARGET = "glfw" Or TARGET = "sexy"
	#GLFW_TARGET = True
#End
#End

#If CONFIG = "debug"
	#DEBUG_PRINT = True
#Else
	#DEBUG_PRINT = False
#End

#DEBUG_PRINT_QUOTES = False

' Imports (Public):
Import byteorder
Import imagedimensions
Import retrostrings
Import stringutil
Import vector

#If CONSOLE_IMPLEMENTED
	Import console
#End

Import sizeof
Import time

#If Not BRL_GAMETARGET_IMPLEMENTED
	Import mojoemulator
#End

' Imports (Private):
Private

' Official:

' BRL:
Import brl.databuffer
Import brl.stream
Import brl.filepath

' Unofficial:

' ImmutableOctet:
Import autostream

Public

' Constant variable(s):

' Used mainly for external code where '-9999...' isn't ever going to be valid.
' You should stick to 'Null' in most cases; try not to use this.
Const NOVAR:Int = -999999

Const ErrorTemplate:String = "[ERROR] {Debug}: " ' + Space
Const LogTemplate:String = "[Info] {Debug}: " ' + Space

' Global variable(s) (Public):
#If CONSOLE_IMPLEMENTED
	Global DebugConsole:Console = Null
#End

' Global variable(s) (Private):
Private

' Nothing so far.

Public

' Functions:
Function FLAG:Int(ID:Int)
	Return Pow(2, ID)
End

' This command is little-endian only:
Function AsByte:Int(I:Int)
	Return ((I Shl 24) Shr 24)
End

' This command treats data as 8-bit (Turns something like $F7, into $FFFFFFF7)
' This is only applied on little-endian systems,
' please use this command for hex values (Monkey converts hex on compile-time).
Function PaddedHex:Int(I:Int)
	If (Not BigEndian()) Then
		Return AsByte(I)
	Endif

	Return I
End

Function Transfer:Bool(InputStream:Stream, OutputStream:Stream, DataSize:Int)
	' Check for errors:
	If (DataSize = 0) Then Return False
	If (InputStream = Null Or InputStream.Eof()) Then Return False
	If (OutputStream = Null) Then Return False

	' Local variable(s):
	#If FLAG_TEMP_BUFFERS
		Local Data:DataBuffer = New DataBuffer(DataSize)
		Local DataLength:= DataSize ' Data.Length()

		' Read from the input-stream, then write to the output-stream:
		InputStream.Read(Data, 0, DataLength)
		OutputStream.Write(Data, 0, DataLength)

		Data.Discard()
	#Else
		'Local Data:Int[DataSize]

		For Local I:Int = 1 To DataSize ' Data.Length()
			OutputStream.WriteByte(InputStream.ReadByte())
		Next
	#End

	' Return the default response.
	Return True
End

Function Sq:Float(Input:Float)
	Return Pow(Input, 2.0) ' Input * Input
End

Function Sq:Int(Input:Int)
	Return Pow(Input, 2)
End

Function WrapAngle:Float(A:Float)
	While (A < 0.0)
		A += 360.0
	Wend

	While (A > 360.0)
		A -= 360.0
	Wend

	Return A
End

Function WrapColor:Float(C:Float)
	#Rem
	While (C < 0.0)
		C += 255.0
	Wend

	While (C > 255.0)
		C -= 255.0
	Wend
	#End

	Return C Mod 256.0
End

Function ResizeBuffer:DataBuffer(Buffer:DataBuffer, Size:Int, CopyData:Bool=True, DiscardOldBuffer:Bool=False)
	' Allocate a new data-buffer.
	Local B:= New DataBuffer(Size)

	' Copy the buffer's bytes over to 'B'.
	If (CopyData) Then
		Buffer.CopyBytes(0, B, 0, Buffer.Length())
	Endif

	If (DiscardOldBuffer) Then
		Buffer.Discard()
	Endif

	' Return the new buffer ('B').
	Return B
End

#If CONSOLE_IMPLEMENTED
	Function DebugBind:Bool(C:Console)
		If (C = Null) Then Return False

		DebugConsole = C

		' Return the default response.
		Return True
	End
#End

Function DebugError:Void(Msg:String, StopExecution:Bool=True)
	#If DEBUG_PRINT
		' Local variable(s):
		Local TempMsg:String

		If (StopExecution) Then
			TempMsg = ErrorTemplate
		Else
			TempMsg = LogTemplate
		Endif

		#If DEBUG_PRINT_QUOTES
			Msg = Tempmsg + Quote + Msg + Quote
		#Else
			Msg = TempMsg + Msg
		#End

		#If CONSOLE_IMPLEMENTED
			If (DebugConsole <> Null) Then
				DebugConsole.WriteLine(Msg, True)
			Else
		#End

		Print(Msg)

		#If CONSOLE_IMPLEMENTED
			Endif
		#End
	#Else
		' This may change later:
		If (StopExecution) Then
			Msg = ErrorTemplate + Msg
		Endif
	#End

	If (StopExecution) Then
		#If CONFIG = "debug"
			#If DEBUG_PRINT
				DebugStop()
			#Else
				Error(Msg)
			#End
		#Else
			#If DEBUG_PRINT
				Local FinalStr:String = (ErrorTemplate + Quote + "Attempt to stop execution failed. (Reason: Release-mode)" + Quote)

				#If CONSOLE_IMPLEMENTED
					If (DebugConsole <> Null) Then
						DebugConsole.WriteLine(FinalStr, True)
					Else
				#End

				Print(FinalStr)

				#If CONSOLE_IMPLEMENTED
					Endif
				#End
			#Else
				Error(Msg)
			#End
		#End
	Endif

	#If CONFIG = "debug"
		' Nothing so far.
	#Else
		' Nothing so far.
	#End

	Return
End

Function DebugPrint:Void(Str:String, StopExecution:Bool=False)
	DebugError(Str, StopExecution)

	Return
End

' Classes:
Class GenericUtilities<T>
	' Global variable(s):
	Global NIL:T

	' Functions:
	Function IndexOfList:T(L:List<T>, Index:Int=0)
		' Local variable(s):
		Local Response:T
		Local Data:list.Node<T> = L.FirstNode()

		For Local I:= 0 Until Index
			Data = Data.NextNode()

			' Not my favorite method, but it works:
			If (Data = Null) Then
				Return Response
			Endif
		Next

		Response = Data.Value()

		' Return the value of the assessed node.
		Return Response
	End

	Function CopyArray:T[](Source:T[], Destination:T[])
		Local MinLength:Int = Min(Source.Length(), Destination.Length())

		For Local Index:Int = 0 Until MinLength
			Destination[Index] = Source[Index]
		Next

		Return Destination
	End

	Function CloneArray:T[](Source:T[], Destination:T[]=[])
		If (Destination.Length() = 0) Then
			Destination = New T[Source.Length()]
		Endif

		Return CopyArray(Source, Destination)
	End

	Function Read:T(S:Stream)
		Return Read(S, NIL)
	End

	Function Read:T[](S:Stream, DataArray:T[], Size:Int=-1)
		If (Size = -1) Then Size = S.ReadInt()

		If (Size > 0) Then
			If (DataArray.Length() = 0) Then
				DataArray = New T[Size]
			Endif
		Endif

		' Local variable(s):
		Local DLength:Int = Min(DataArray.Length, Size)

		For Local Index:= 0 Until DLength
			DataArray[Index] = Read(S, NIL)
		Next

		Return DataArray
	End

	Function Write:Void(S:Stream, DataArray:T[], WriteLength:Bool=True)
		' Local variable(s):
		Local DLength:= DataArray.Length()

		If (WriteLength) Then S.WriteInt(DLength)

		For Local Index:= 0 Until DLength
			Write(S, DataArray[Index])
		Next

		Return
	End

	Function Read:Int(S:Stream, Indentifier:Int, Size:Int=4)
		' Local variable(s):
		Local Data:Int = 0

		If (Size > 1) Then
			If (Size < 4) Then
				Data = S.ReadShort()
			Else
				If (Size > 4) Then
					S.ReadInt()
				Endif

				Data = S.ReadInt()
			Endif
		Else
			Data = S.ReadByte()
		Endif

		Return Data
	End

	Function Write:Void(S:Stream, Data:Int, Size:Int=4)
		Size = IntSize(Size)

		If (Size > 1) Then
			If (Size < 4) Then
				S.WriteShort(Data)
			Else
				If (Size > 4) Then
					S.WriteInt(0)
				Endif

				S.WriteInt(Data)
			Endif
		Else
			S.WriteByte(Data)
		Endif

		Return
	End

	#Rem
	Function Read:Bool(S:Stream, Identifier:Bool)
		Return Read(S, Int(Identifier), 1)
	End

	Function Write:Void(S:Stream, B:Bool)
		Write(S, Int(B), 1)

		Return
	End
	#End

	Function Read:Float(S:Stream, Identifier:Float)
		Return S.ReadFloat()
	End

	Function Write:Void(S:Stream, Data:Float)
		S.WriteFloat(Data)

		Return
	End

	Function Read:String(S:Stream, Data:String, Encoding:String="utf8", Size:Int=-1)
		If (Size = -1) Then Size = S.ReadInt()

		Data = S.ReadString(Size, Encoding)

		' Return the newly read string.
		Return Data
	End

	Function Write:Void(S:Stream, Data:String, Encoding:String="utf8", WriteSize:Bool=True)
		If (WriteSize) Then S.WriteInt(Data.Length())
		S.WriteString(Data, Encoding)

		Return
	End
End


Updated example to use VirtualRes

Updated example to use VirtualRes

--HG--
extra : convert_revision : svn%3Ae934d3cd-0d57-f34a-94d0-9e8d31ae1c29/trunk%40166

Strict

Import diddy

Function Main:Int()
	game = New MyGame()
	Return 0
End Function

Global titleScreen:TitleScreen
Global gameScreen:GameScreen
Global optionScreen:OptionScreen

Class MyGame Extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()
		SetScreenSize(1024, 768)
		drawFPSOn = True

		titleScreen = New TitleScreen
		gameScreen = New GameScreen
		optionScreen = New OptionScreen

		titleScreen.PreStart()
		Return 0
	End
End

Class TitleScreen Extends Screen
	Field menu:SimpleMenu
	Field musicFormat:String

	Method New()
		name = "TitleScreen"
	End

	Method Start:Void()
		game.screenFade.Start(50, False)
		menu = New SimpleMenu("ButtonOver", "ButtonClick", 0, 0, 10, False)
		Local b:SimpleButton = menu.AddButton("newgame.png", "newgameMO.png")
		b = menu.AddButton("continue.png", "continueMO.png")
		b = menu.AddButton("options.png", "optionsMO.png")
		b = menu.AddButton("quit.png", "quitMO.png")

#If TARGET="glfw"
		musicFormat="wav"
#Elseif TARGET="html5"
		musicFormat="ogg"
#Elseif TARGET="flash"
		musicFormat="mp3"
#Elseif TARGET="android"
		musicFormat="ogg"
#Elseif TARGET="xna"
		musicFormat="wav"
#Elseif TARGET="ios"
		musicFormat="m4a"
#End

		game.MusicPlay("happy."+musicFormat, True)
		menu.Centre()
	End

	Method Render:Void()
		Cls
		DrawText "TITLE SCREEN", SCREEN_WIDTH2, 10, 0.5, 0.5

	End

	Method ExtraRender:Void()
		menu.Draw()
	End

	Method Update:Void()
		menu.Update()

		If menu.Clicked("newgame") Then
			game.screenFade.Start(50, True)
			game.nextScreen = gameScreen
		End

		If menu.Clicked("options") Then
			game.screenFade.Start(50, True)
			game.nextScreen = optionScreen
		End

		If menu.Clicked("continue") Then
			game.screenFade.Start(50, True)
			game.nextScreen = titleScreen
		End

		If KeyHit(KEY_ESCAPE) Or menu.Clicked("quit")
			game.screenFade.Start(50, True)
			game.nextScreen = game.exitScreen
		End
	End
End

Class GameScreen Extends Screen
	Field menu:SimpleMenu

	Method Start:Void()
		game.screenFade.Start(50, False)
		menu = New SimpleMenu("ButtonOver", "ButtonClick", 0, 0, 10, True)
		Local b:SimpleButton = menu.AddButton("quit.png", "quitMO.png")
		menu.Centre()
	End

	Method Render:Void()
		Cls
		DrawText "GAME SCREEN", SCREEN_WIDTH2, 10, 0.5, 0.5
		menu.Draw()
	End

	Method Update:Void()
		menu.Update()

		If menu.Clicked("quit") Then
			game.screenFade.Start(50, True)
			game.nextScreen = titleScreen
		End
	End
End

Class OptionScreen Extends Screen
	Field menu:SimpleMenu
	Field musicSlider:SimpleSlider

	Method Start:Void()
		game.screenFade.Start(50, False)
		menu = New SimpleMenu("ButtonOver", "ButtonClick", 0, 0, 10, True)
		Local b:SimpleButton = menu.AddButton("quit.png", "quitMO.png")
		menu.Centre()

		musicSlider = New SimpleSlider("slider_bar.png", "slider.png", SCREEN_WIDTH2 - 93, 115, 35, "music", 20, True)
		musicSlider.SetValue(game.musicVolume)
	End

	Method Render:Void()
		Cls
		DrawText "OPTION SCREEN", SCREEN_WIDTH2, 10, 0.5, 0.5
		menu.Draw()
		DrawText "MUSIC VOLUME:", musicSlider.x + (musicSlider.image.w / 2)  , musicSlider.y - 20, 0.5
		musicSlider.Draw()
	End

	Method Update:Void()
		menu.Update()

		If musicSlider.Update() Then
			game.MusicSetVolume(musicSlider.value)
		End If

		If menu.Clicked("quit") Then
			game.screenFade.Start(50, True)
			game.nextScreen = titleScreen
		End
	End
End

Strict

Import diddy

Function Main:Int()
	game = new MyGame()
	Return 0
End Function

Global titleScreen:TitleScreen
Global gameScreen:GameScreen
Global optionScreen:OptionScreen

Class MyGame extends DiddyApp
	Method OnCreate:Int()
		Super.OnCreate()

		drawFPSOn = True

		titleScreen = New TitleScreen
		gameScreen = New GameScreen
		optionScreen = new OptionScreen

		titleScreen.PreStart()
		return 0
	End
End

Class TitleScreen Extends Screen
	Field menu:SimpleMenu
	Field musicFormat:String

	Method New()
		name = "TitleScreen"
	End

	Method Start:Void()
		game.screenFade.Start(50, false)
		menu = New SimpleMenu("ButtonOver", "ButtonClick", 0, 0, 10)
		Local b:SimpleButton = menu.AddButton("newgame.png", "newgameMO.png")
		b = menu.AddButton("continue.png", "continueMO.png")
		b = menu.AddButton("options.png", "optionsMO.png")
		b = menu.AddButton("quit.png", "quitMO.png")

#If TARGET="glfw"
		musicFormat="wav"
#Elseif TARGET="html5"
		musicFormat="ogg"
#Elseif TARGET="flash"
		musicFormat="mp3"
#Elseif TARGET="android"
		musicFormat="ogg"
#Elseif TARGET="xna"
		musicFormat="wav"
#Elseif TARGET="ios"
		musicFormat="m4a"
#End

		game.MusicPlay("happy."+musicFormat, True)
		menu.Centre()
	End

	Method Render:Void()
		Cls
		DrawText "TITLE SCREEN", SCREEN_WIDTH2, 10, 0.5, 0.5
		menu.Draw()
	End

	Method Update:Void()
		menu.Update()

		If menu.Clicked("newgame") Then
			game.screenFade.Start(50, true)
			game.nextScreen = gameScreen
		End

		If menu.Clicked("options") Then
			game.screenFade.Start(50, true)
			game.nextScreen = optionScreen
		End

		If menu.Clicked("continue") Then
			game.screenFade.Start(50, true)
			game.nextScreen = titleScreen
		End

		If KeyHit(KEY_ESCAPE) or menu.Clicked("quit")
			game.screenFade.Start(50, true)
			game.nextScreen = game.exitScreen
		End
	End
End

Class GameScreen Extends Screen
	Field menu:SimpleMenu

	Method Start:Void()
		game.screenFade.Start(50, false)
		menu = New SimpleMenu("ButtonOver", "ButtonClick", 0, 0, 10)
		Local b:SimpleButton = menu.AddButton("quit.png", "quitMO.png")
		menu.Centre()
	End

	Method Render:Void()
		Cls
		DrawText "GAME SCREEN", SCREEN_WIDTH2, 10, 0.5, 0.5
		menu.Draw()
	End

	Method Update:Void()
		menu.Update()

		If menu.Clicked("quit") Then
			game.screenFade.Start(50, true)
			game.nextScreen = titleScreen
		End
	End
End

Class OptionScreen Extends Screen
	Field menu:SimpleMenu
	Field musicSlider:SimpleSlider

	Method Start:Void()
		game.screenFade.Start(50, false)
		menu = New SimpleMenu("ButtonOver", "ButtonClick", 0, 0, 10)
		Local b:SimpleButton = menu.AddButton("quit.png", "quitMO.png")
		menu.Centre()

		musicSlider = New SimpleSlider("slider_bar.png", "slider.png", SCREEN_WIDTH2 - 93, 115, 35, "music", 20)
		musicSlider.SetValue(game.musicVolume)
	End

	Method Render:Void()
		Cls
		DrawText "OPTION SCREEN", SCREEN_WIDTH2, 10, 0.5, 0.5
		menu.Draw()
		DrawText "MUSIC VOLUME:", musicSlider.x + (musicSlider.image.w / 2)  , musicSlider.y - 20, 0.5
		musicSlider.Draw()
	End

	Method Update:Void()
		menu.Update()

		If musicSlider.Update() Then
			game.MusicSetVolume(musicSlider.value)
		End If

		If menu.Clicked("quit") Then
			game.screenFade.Start(50, true)
			game.nextScreen = titleScreen
		End
	End
End


fix scaled graphics.Sprite in graphics.Align

Strict

Private

Import bono.src.utils
Import positionable
Import sizeable
Import sprite

Public

Class Align Abstract
    Const TOP:Int = 0
    Const BOTTOM:Int = 1
    Const LEFT:Int = 2
    Const RIGHT:Int = 3
    Const CENTER:Int = 4

    Function Horizontal:Void(object:Object, mode:Int)
        CheckInterfaces(object)
        Local scale:Float = GetScale(object).x

        Select mode
        Case LEFT
            ' Default alignment - nothing to do here
        Case RIGHT
            Positionable(object).GetPosition().x -= Sizeable(object).GetSize().x * scale
        Case CENTER
            Positionable(object).GetPosition().x -= Sizeable(object).GetSize().x * scale / 2
        Default
            Error("Invalid alignment mode (" + mode + ") given")
        End
    End

    Function Vertical:Void(object:Object, mode:Int)
        CheckInterfaces(object)
        Local scale:Float = GetScale(object).y

        Select mode
        Case TOP
            ' Default alignment - nothing to do here
        Case BOTTOM
            Positionable(object).GetPosition().y -= Sizeable(object).GetSize().y * scale
        Case CENTER
            Positionable(object).GetPosition().y -= Sizeable(object).GetSize().y * scale / 2
        Default
            Error("Invalid alignment mode (" + mode + ") given")
        End
    End

    Function Centered:Void(object:Sizeable)
        Horizontal(object, CENTER)
        Vertical(object, CENTER)
    End

    Private

    Function CheckInterfaces:Void(object:Object)
        If Not Sizeable(object)
            Error("Given object must implement Sizeable")
        End

        If Not Positionable(object)
            Error("Given object must implement Positionable")
        End
    End

    Function GetScale:Vector2D(object:Object)
        If Sprite(object) Then Return Sprite(object).scale
        Return New Vector2D(1, 1)
    End
End

Strict

Private

Import bono.src.utils
Import positionable
Import sizeable
Import sprite

Public

Class Align Abstract
    Const TOP:Int = 0
    Const BOTTOM:Int = 1
    Const LEFT:Int = 2
    Const RIGHT:Int = 3
    Const CENTER:Int = 4

    Function Horizontal:Void(object:Object, mode:Int)
        CheckInterfaces(object)
        Local scale:Float = GetScale(object).x

        Select mode
        Case LEFT
            ' Default alignment - nothing to do here
        Case RIGHT
            Positionable(object).GetPosition().x -= Sizeable(object).GetSize().x / scale
        Case CENTER
            Positionable(object).GetPosition().x -= Sizeable(object).GetSize().x / scale / 2
        Default
            Error("Invalid alignment mode (" + mode + ") given")
        End
    End

    Function Vertical:Void(object:Object, mode:Int)
        CheckInterfaces(object)
        Local scale:Float = GetScale(object).y

        Select mode
        Case TOP
            ' Default alignment - nothing to do here
        Case BOTTOM
            Positionable(object).GetPosition().y -= Sizeable(object).GetSize().y / scale
        Case CENTER
            Positionable(object).GetPosition().y -= Sizeable(object).GetSize().y / scale / 2
        Default
            Error("Invalid alignment mode (" + mode + ") given")
        End
    End

    Function Centered:Void(object:Sizeable)
        Horizontal(object, CENTER)
        Vertical(object, CENTER)
    End

    Private

    Function CheckInterfaces:Void(object:Object)
        If Not Sizeable(object)
            Error("Given object must implement Sizeable")
        End

        If Not Positionable(object)
            Error("Given object must implement Positionable")
        End
    End

    Function GetScale:Vector2D(object:Object)
        If Sprite(object) Then Return Sprite(object).scale
        Return New Vector2D(1, 1)
    End
End