MathHelper.Round() fixed

MathHelper.Round() fixed

Strict

Public

Class MathHelper Abstract
    Function Round:Int(given:Float)
        If given > 0.5 Then Return Ceil(given)
        Return Floor(given)
    End

    Function ModF:Float(left:Float, right:Float)
        Return left - right * Floor(left / right)
    End

    Function ModI:Int(left:Int, right:Int)
        Return left Mod right
    End
End

Strict

Public

Class MathHelper Abstract
    Function Round:Int(given:Float)
        If given > 0.5 Then Return Floor(given)
        Return Ceil(given)
    End

    Function ModF:Float(left:Float, right:Float)
        Return left - right * Floor(left / right)
    End

    Function ModI:Int(left:Int, right:Int)
        Return left Mod right
    End
End


add Vector2D tests for Floor(), Round() and Ceil()

Strict

Private

Import bono

Public

Class Vector2DTest Extends TestCase
    Method TestInstantiate:Void()
        Local vec:Vector2D = New Vector2D(12, 34)
        AssertEquals(12.0, vec.x)
        AssertEquals(34.0, vec.y)
    End

    Method TestInstantiateWithoutArguments:Void()
        Local vec:Vector2D = New Vector2D()
        AssertEquals(0.0, vec.x)
        AssertEquals(0.0, vec.y)
    End

    Method TestIsZero:Void()
        Local zeroVec:Vector2D = New Vector2D(0, 0)
        AssertTrue(zeroVec.IsZero())

        Local nonZeroVec:Vector2D = New Vector2D(1, 2)
        AssertFalse(nonZeroVec.IsZero())
    End

    Method TestLength:Void()
        Local vec:Vector2D = New Vector2D(10, 0)
        AssertEquals(10.0, vec.Length())

        vec = New Vector2D(0, 5)
        AssertEquals(5.0, vec.Length())
    End

    Method TestDotProduct:Void()
        Local vecA:Vector2D = New Vector2D(1, 1)
        Local vecB:Vector2D = New Vector2D(3, 5)

        AssertEquals(8.0, vecA.DotProduct(vecB))
    End

    Method TestCrossProduct:Void()
        Local vecA:Vector2D = New Vector2D(1, 1)
        Local vecB:Vector2D = New Vector2D(3, 5)

        AssertEquals(2.0, vecA.CrossProduct(vecB))
    End

    Method TestDistance:Void()
        Local vecA:Vector2D = New Vector2D(1, 2)
        Local vecB:Vector2D = New Vector2D(3, 4)

        AssertEquals(2.8284270763397217, vecA.Distance(vecB))
    End

    Method TestDistanceWithSecondVectorNotGreater:Void()
        Local vecA:Vector2D = New Vector2D(1, 2)
        Local vecB:Vector2D = New Vector2D(3, 4)

        AssertEquals(2.8284270763397217, vecB.Distance(vecA))
    End

    Method TestEqual:Void()
        Local firstVec:Vector2D = New Vector2D(12, 34)
        AssertTrue(firstVec.Equal(firstVec))

        Local secondVec:Vector2D = New Vector2D(56, 78)
        AssertFalse(firstVec.Equal(secondVec))
    End

    Method TestCopy:Void()
        Local vec:Vector2D = New Vector2D(12, 34)
        Local copyVec:Vector2D = vec.Copy()

        AssertTrue(vec.Equal(copyVec))
    End

    Method TestSet:Void()
        Local vec:Vector2D = New Vector2D(12, 34)
        Local copyVec:Vector2D = New Vector2D(0, 0)
        copyVec.Set(vec)

        AssertTrue(vec.Equal(copyVec))
    End

    Method TestNormalize:Void()
        Local vec:Vector2D = New Vector2D(1, 2)
        vec.Normalize()

        AssertEquals(0.44721359014511108, vec.x)
        AssertEquals(0.89442718029022217, vec.y)
    End

    Method TestRotate:Void()
        Local vec:Vector2D = New Vector2D(1, 2)
        vec.Rotate(90)

        AssertEquals(-2.0, vec.x)
        AssertEquals(-1.0000001192092896, vec.y)
    End

    Method TestRevert:Void()
        Local vec:Vector2D = New Vector2D(1, 2)
        vec.Revert()

        AssertEquals(-1.0, vec.x)
        AssertEquals(-2.0, vec.y)
    End

    Method TestAdd:Void()
        Local vec:Vector2D = New Vector2D(0, 0)
        vec.Add(New Vector2D(4, 5))

        AssertEquals(4.0, vec.x)
        AssertEquals(5.0, vec.y)
    End

    Method TestAddFactor:Void()
        Local vec:Vector2D = New Vector2D(0, 0)
        vec.Add(2.5)

        AssertEquals(2.5, vec.x)
        AssertEquals(2.5, vec.y)
    End

    Method TestSub:Void()
        Local vec:Vector2D = New Vector2D(0, 0)
        vec.Sub(New Vector2D(4, 5))

        AssertEquals(-4.0, vec.x)
        AssertEquals(-5.0, vec.y)
    End

    Method TestSubFactor:Void()
        Local vec:Vector2D = New Vector2D(0, 0)
        vec.Sub(2.5)

        AssertEquals(-2.5, vec.x)
        AssertEquals(-2.5, vec.y)
    End

    Method TestMul:Void()
        Local vec:Vector2D = New Vector2D(2, 2)
        vec.Mul(New Vector2D(2, 3))

        AssertEquals(4.0, vec.x)
        AssertEquals(6.0, vec.y)
    End

    Method TestMulFactor:Void()
        Local vec:Vector2D = New Vector2D(2, 3)
        vec.Mul(2)

        AssertEquals(4.0, vec.x)
        AssertEquals(6.0, vec.y)
    End

    Method TestDiv:Void()
        Local vec:Vector2D = New Vector2D(4, 6)
        vec.Div(New Vector2D(2, 3))

        AssertEquals(2.0, vec.x)
        AssertEquals(2.0, vec.y)
    End

    Method TestDivFactor:Void()
        Local vec:Vector2D = New Vector2D(8, 12)
        vec.Div(2)

        AssertEquals(4.0, vec.x)
        AssertEquals(6.0, vec.y)
    End

    Method TestToString:Void()
        Local vec:Vector2D = New Vector2D(12, 34)

        AssertEquals("(12.0, 34.0)", vec.ToString())
    End

    Method TestFloor:Void()
        Local vec:Vector2D = New Vector2D(1.3, 2.9)
        vec.Floor()

        AssertEquals(1.0, vec.x)
        AssertEquals(2.0, vec.y)
    End

    Method TestCeil:Void()
        Local vec:Vector2D = New Vector2D(1.3, 2.9)
        vec.Ceil()

        AssertEquals(2.0, vec.x)
        AssertEquals(3.0, vec.y)
    End

    Method TestRound:Void()
        Local vec:Vector2D = New Vector2D(1.4, 1.6)
        vec.Round()

        AssertEquals(1.0, vec.x)
        AssertEquals(2.0, vec.y)
    End
End

Strict

Private

Import bono

Public

Class Vector2DTest Extends TestCase
    Method TestInstantiate:Void()
        Local vec:Vector2D = New Vector2D(12, 34)
        AssertEquals(12.0, vec.x)
        AssertEquals(34.0, vec.y)
    End

    Method TestInstantiateWithoutArguments:Void()
        Local vec:Vector2D = New Vector2D()
        AssertEquals(0.0, vec.x)
        AssertEquals(0.0, vec.y)
    End

    Method TestIsZero:Void()
        Local zeroVec:Vector2D = New Vector2D(0, 0)
        AssertTrue(zeroVec.IsZero())

        Local nonZeroVec:Vector2D = New Vector2D(1, 2)
        AssertFalse(nonZeroVec.IsZero())
    End

    Method TestLength:Void()
        Local vec:Vector2D = New Vector2D(10, 0)
        AssertEquals(10.0, vec.Length())

        vec = New Vector2D(0, 5)
        AssertEquals(5.0, vec.Length())
    End

    Method TestDotProduct:Void()
        Local vecA:Vector2D = New Vector2D(1, 1)
        Local vecB:Vector2D = New Vector2D(3, 5)

        AssertEquals(8.0, vecA.DotProduct(vecB))
    End

    Method TestCrossProduct:Void()
        Local vecA:Vector2D = New Vector2D(1, 1)
        Local vecB:Vector2D = New Vector2D(3, 5)

        AssertEquals(2.0, vecA.CrossProduct(vecB))
    End

    Method TestDistance:Void()
        Local vecA:Vector2D = New Vector2D(1, 2)
        Local vecB:Vector2D = New Vector2D(3, 4)

        AssertEquals(2.8284270763397217, vecA.Distance(vecB))
    End

    Method TestDistanceWithSecondVectorNotGreater:Void()
        Local vecA:Vector2D = New Vector2D(1, 2)
        Local vecB:Vector2D = New Vector2D(3, 4)

        AssertEquals(2.8284270763397217, vecB.Distance(vecA))
    End

    Method TestEqual:Void()
        Local firstVec:Vector2D = New Vector2D(12, 34)
        AssertTrue(firstVec.Equal(firstVec))

        Local secondVec:Vector2D = New Vector2D(56, 78)
        AssertFalse(firstVec.Equal(secondVec))
    End

    Method TestCopy:Void()
        Local vec:Vector2D = New Vector2D(12, 34)
        Local copyVec:Vector2D = vec.Copy()

        AssertTrue(vec.Equal(copyVec))
    End

    Method TestSet:Void()
        Local vec:Vector2D = New Vector2D(12, 34)
        Local copyVec:Vector2D = New Vector2D(0, 0)
        copyVec.Set(vec)

        AssertTrue(vec.Equal(copyVec))
    End

    Method TestNormalize:Void()
        Local vec:Vector2D = New Vector2D(1, 2)
        vec.Normalize()

        AssertEquals(0.44721359014511108, vec.x)
        AssertEquals(0.89442718029022217, vec.y)
    End

    Method TestRotate:Void()
        Local vec:Vector2D = New Vector2D(1, 2)
        vec.Rotate(90)

        AssertEquals(-2.0, vec.x)
        AssertEquals(-1.0000001192092896, vec.y)
    End

    Method TestRevert:Void()
        Local vec:Vector2D = New Vector2D(1, 2)
        vec.Revert()

        AssertEquals(-1.0, vec.x)
        AssertEquals(-2.0, vec.y)
    End

    Method TestAdd:Void()
        Local vec:Vector2D = New Vector2D(0, 0)
        vec.Add(New Vector2D(4, 5))

        AssertEquals(4.0, vec.x)
        AssertEquals(5.0, vec.y)
    End

    Method TestAddFactor:Void()
        Local vec:Vector2D = New Vector2D(0, 0)
        vec.Add(2.5)

        AssertEquals(2.5, vec.x)
        AssertEquals(2.5, vec.y)
    End

    Method TestSub:Void()
        Local vec:Vector2D = New Vector2D(0, 0)
        vec.Sub(New Vector2D(4, 5))

        AssertEquals(-4.0, vec.x)
        AssertEquals(-5.0, vec.y)
    End

    Method TestSubFactor:Void()
        Local vec:Vector2D = New Vector2D(0, 0)
        vec.Sub(2.5)

        AssertEquals(-2.5, vec.x)
        AssertEquals(-2.5, vec.y)
    End

    Method TestMul:Void()
        Local vec:Vector2D = New Vector2D(2, 2)
        vec.Mul(New Vector2D(2, 3))

        AssertEquals(4.0, vec.x)
        AssertEquals(6.0, vec.y)
    End

    Method TestMulFactor:Void()
        Local vec:Vector2D = New Vector2D(2, 3)
        vec.Mul(2)

        AssertEquals(4.0, vec.x)
        AssertEquals(6.0, vec.y)
    End

    Method TestDiv:Void()
        Local vec:Vector2D = New Vector2D(4, 6)
        vec.Div(New Vector2D(2, 3))

        AssertEquals(2.0, vec.x)
        AssertEquals(2.0, vec.y)
    End

    Method TestDivFactor:Void()
        Local vec:Vector2D = New Vector2D(8, 12)
        vec.Div(2)

        AssertEquals(4.0, vec.x)
        AssertEquals(6.0, vec.y)
    End

    Method TestToString:Void()
        Local vec:Vector2D = New Vector2D(12, 34)

        AssertEquals("(12.0, 34.0)", vec.ToString())
    End
End


debug log message fixed

debug log message fixed

Strict

#If TARGET="ios"

' ============================
' == COMMERCIAL BREAK START ==
' ============================
'
' Tired of setting things up? Monkey-Wizard to the rescue!
' --> https://github.com/michaelcontento/monkey-wizard
'
' And the whole thing should be easy as:
' --> wizard IosAppirater ../myproject/myproject.build/ios
'
' ============================
' ==  COMMERCIAL BREAK END  ==
' ============================
'
'
' You need to include the following frameworks to your project:
'   * CFNetwork.framework
'   * SystemConfiguration.framework
'   * StoreKit.framework
'   * Appirater (=> https://github.com/arashpayan/appirater)

Private

Import "native/appirater.ios.cpp"

Extern

Class Appirater Abstract
    Function Launched:Void(canPrompt:Bool=True)="AppiraterBridge::Launched"
    Function EnteredForeground:Void(canPrompt:Bool=True)="AppiraterBridge::EnteredForeground"
    Function UserDidSignificantEvent:Void(canPrompt:Bool=True)="AppiraterBridge::UserDidSignificantEvent"
    Function SetAppId:Void(id:String)="AppiraterBridge::SetAppId"
    Function SetDaysUntilPrompt:Void(days:Int)="AppiraterBridge::SetDaysUntilPrompt"
    Function SetUsesUntilPrompt:Void(days:Int)="AppiraterBridge::SetUsesUntilPrompt"
    Function SetSignificantEventsUntilPrompt:Void(uses:Int)="AppiraterBridge::SetSignificantEventsUntilPrompt"
    Function SetTimeBeforeReminding:Void(time:Int)="AppiraterBridge::SetTimeBeforeReminding"
    Function SetDebug:Void(flag:Bool)="AppiraterBridge::SetDebug"
End

#Else

Private

Import bono

Public

Class Appirater Abstract
    Function Launched:Void(canPrompt:Bool=True)
        If Target.IS_DEBUG Then Print "[Appirater Launched] canPrompt: " + BoolToString(canPrompt)
    End

    Function EnteredForeground:Void(canPrompt:Bool=True)
        If Target.IS_DEBUG Then Print "[Appirater EnteredForeground] canPrompt: " + BoolToString(canPrompt)
    End

    Function UserDidSignificantEvent:Void(canPrompt:Bool=True)
        If Target.IS_DEBUG Then Print "[Appirater UserDidSignificantEvent] canPrompt: " + BoolToString(canPrompt)
    End

    Function SetAppId:Void(id:String)
        If Target.IS_DEBUG Then Print "[Appirater SetAppId] id: " + id
    End

    Function SetDaysUntilPrompt:Void(days:Int)
        If Target.IS_DEBUG Then Print "[Appirater SetDaysUntilPrompt] days: " + days
    End

    Function SetUsesUntilPrompt:Void(days:Int)
        If Target.IS_DEBUG Then Print "[Appirater SetUsesUntilPrompt] days: " + days
    End

    Function SetSignificantEventsUntilPrompt:Void(uses:Int)
        If Target.IS_DEBUG Then Print "[Appirater SetSignificantEventsUntilPrompt] uses: " + uses
    End

    Function SetTimeBeforeReminding:Void(time:Int)
        If Target.IS_DEBUG Then Print "[Appirater SetTimeBeforeReminding] time: " + time
    End

    Function SetDebug:Void(flag:Bool)
        If Target.IS_DEBUG Then Print "[Appirater SetDebug] flag: " + BoolToString(flag)
    End

    Private

    Function BoolToString:String(flag:Bool)
        If flag Then Return "YES"
        Return "NO"
    End
End

#End

Strict

#If TARGET="ios"

' ============================
' == COMMERCIAL BREAK START ==
' ============================
'
' Tired of setting things up? Monkey-Wizard to the rescue!
' --> https://github.com/michaelcontento/monkey-wizard
'
' And the whole thing should be easy as:
' --> wizard IosAppirater ../myproject/myproject.build/ios
'
' ============================
' ==  COMMERCIAL BREAK END  ==
' ============================
'
'
' You need to include the following frameworks to your project:
'   * CFNetwork.framework
'   * SystemConfiguration.framework
'   * StoreKit.framework
'   * Appirater (=> https://github.com/arashpayan/appirater)

Private

Import "native/appirater.ios.cpp"

Extern

Class Appirater Abstract
    Function Launched:Void(canPrompt:Bool=True)="AppiraterBridge::Launched"
    Function EnteredForeground:Void(canPrompt:Bool=True)="AppiraterBridge::EnteredForeground"
    Function UserDidSignificantEvent:Void(canPrompt:Bool=True)="AppiraterBridge::UserDidSignificantEvent"
    Function SetAppId:Void(id:String)="AppiraterBridge::SetAppId"
    Function SetDaysUntilPrompt:Void(days:Int)="AppiraterBridge::SetDaysUntilPrompt"
    Function SetUsesUntilPrompt:Void(days:Int)="AppiraterBridge::SetUsesUntilPrompt"
    Function SetSignificantEventsUntilPrompt:Void(uses:Int)="AppiraterBridge::SetSignificantEventsUntilPrompt"
    Function SetTimeBeforeReminding:Void(time:Int)="AppiraterBridge::SetTimeBeforeReminding"
    Function SetDebug:Void(flag:Bool)="AppiraterBridge::SetDebug"
End

#Else

Private

Import bono

Public

Class Appirater Abstract
    Function Launched:Void(canPrompt:Bool=True)
        If Target.IS_DEBUG Then Print "[Appirater Launched] canPrompt: " + BoolToString(canPrompt)
    End

    Function EnteredForeground:Void(canPrompt:Bool=True)
        If Target.IS_DEBUG Then Print "[Appirater EnteredForeground] canPrompt: " + BoolToString(canPrompt)
    End

    Function UserDidSignificantEvent:Void(canPrompt:Bool=True)
        If Target.IS_DEBUG Then Print "[Appirater UserDidSignificantEvent] canPrompt: " + BoolToString(canPrompt)
    End

    Function SetAppId:Void(id:String)
        If Target.IS_DEBUG Then Print "[Appirater SetAppId id: ]" + id
    End

    Function SetDaysUntilPrompt:Void(days:Int)
        If Target.IS_DEBUG Then Print "[Appirater SetDaysUntilPrompt] days: " + days
    End

    Function SetUsesUntilPrompt:Void(days:Int)
        If Target.IS_DEBUG Then Print "[Appirater SetUsesUntilPrompt] days: " + days
    End

    Function SetSignificantEventsUntilPrompt:Void(uses:Int)
        If Target.IS_DEBUG Then Print "[Appirater SetSignificantEventsUntilPrompt] uses: " + uses
    End

    Function SetTimeBeforeReminding:Void(time:Int)
        If Target.IS_DEBUG Then Print "[Appirater SetTimeBeforeReminding] time: " + time
    End

    Function SetDebug:Void(flag:Bool)
        If Target.IS_DEBUG Then Print "[Appirater SetDebug] flag: " + BoolToString(flag)
    End

    Private

    Function BoolToString:String(flag:Bool)
        If flag Then Return "YES"
        Return "NO"
    End
End

#End


Added function-wrappers for the MD5 classes.

#Rem
	NOTES:
		* Please do not use the standard 'MD5Hasher' class for 'DataBuffers' and 'Streams'.
		These must be handled by their respective "hashers". Other than that, you should be good.

		* The 'MD5Hasher' class was developed with arrays (Integer arrays) in mind, but by extension, 'Strings' do work. (Very well, I might add)

		* To use this module simply do the following:

		Rough example:

		MD5HasherClassHere.Run(DataHere)

		Real world example:

		' To calculate the hash of a string, do this.
		MD5Hasher<String>.Run(Str)

		Alternatively, you can call 'MD5HasherClassHere.Instance' in order to configure the global instance.
		Or, you could always create your own instance of the class, and configure it from there.

		For the sake of benchmarking, it's recommended that you just use the global instance as you see fit. (Through 'Run', or 'Instance')

		* Standard 'Stream' support is implemented, but not completely tested.
		It's a dirty hack, but it does the job well enough. Use at your own risk.

		* All functionality present should be in working order, and follow standard MD5 behaviour.
		If you get undesired results, please let me know. Please note that on the standard C++ targets (Targets using Monkey's garbage collector),
		this implementation tends to be bottlenecked by your GC settings.

		Some things are generated for the sake of memory efficiency. This was built with limited memory environments in mind.
		In fact part of why I made this was to test out Monkey performance-wise on embedded ARM systems.

		Most things which are generated are specific to the input, and because of this, they can not be cached properly.
		(Without some reworking which I may or may not do)
#End

Strict

Public

' Preprocessor related:
#MD5_STREAM_CACHE_MODE_ARRAY		= 0
#MD5_STREAM_CACHE_MODE_STRING		= 1
#MD5_STREAM_CACHE_MODE_BUFFER		= 2

#If TARGET = "android"
	#MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY ' MD5_STREAM_CACHE_MODE_STRING
#Else
	#MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY ' MD5_STREAM_CACHE_MODE_BUFFER
#End

' Imports (External):
Import util
Import sizeof

' Standard BRL modules:
Import brl.stream
Import brl.databuffer

' Imports (Internal):

' External language bindings.
Import external

' Global variable(s):
' Nothing so far.

' Functions:

' Quick wrappers for the standard classes' 'Run' commands:
Function MD5:String(Data:DataBuffer)
	Return MD5BufferHasher.Run(Data)
End

Function MD5:String(S:Stream)
	Return MD5StreamHasher.Run(S)
End

Function MD5:String(IA:Int[])
	Return MD5Hasher<Int[]>.Run(IA)
End

Function MD5:String(Str:String)
	Return MD5Hasher<String>.Run(Str)
End

' This is effectively just a really quick rewrite of Java's 'hashCode' method:
Function HashCode:Int(S:String)
	' Local variable(s):
	Local Hash:Int = 0

	' Compute the "hash code":
	For Local I:= 0 Until S.Length()
		Hash = 31*Hash + S[I]
	Next

	' Return the resulting hash.
	Return Hash
End

Function RotateLeft:Int(Value:Int, ShiftBits:Int)
	Return Lsl(Value, ShiftBits) | Lsr( Value, (SizeOf_Integer_InBits - ShiftBits))
	'Return (Value Shl ShiftBits) | (Value Shr (32-ShiftBits))
End

' Classes:
Class MD5Data
	' Constant variable(s):

	' Defaults:

	' Booleans / Flags:
	Const Default_InitKTable:Bool = True
	Const Default_InitShiftTable:Bool = True

	' Global variable(s):
	' Nothing so far.

	' Functions:
	' Nothing so far.

	' Constructor(s):
	Method New(MakeKTable:Bool=Default_InitKTable, MakeShiftTable:Bool=Default_InitShiftTable)
		If (MakeKTable) Then
			InitKTable()
		Endif

		If (MakeShiftTable) Then
			InitShiftTable()
		Endif
	End

	' Destructor(s):
	Method Free:Bool()
		K = []
		Shift = []

		' Return the default response.
		Return True
	End

	' Methods:
	Method InitKTable:Int[]()
		'K = [-680876936,-389564586,606105819,-1044525330,-176418897,1200080426,-1473231341,-45705983,1770035416,-1958414417,-42063,-1990404162,1804603682,-40341101,-1502002290,1236535329,-165796510,-1069501632,643717713,-373897302,-701558691,38016083,-660478335,-405537848,568446438,-1019803690,-187363961,1163531501,-1444681467,-51403784,1735328473,-1926607734,-378558,-2022574463,1839030562,-35309556,-1530992060,1272893353,-155497632,-1094730640,681279174,-358537222,-722521979,76029189,-640364487,-421815835,530742520,-995338651,-198630844,1126891415,-1416354905,-57434055,1700485571,-1894986606,-1051523,-2054922799,1873313359,-30611744,-1560198380,1309151649,-145523070,-1120210379,718787259,-343485551]
		K = [$d76aa478, $e8c7b756, $242070db, $c1bdceee, $f57c0faf, $4787c62a, $a8304613, $fd469501, $698098d8, $8b44f7af, $ffff5bb1, $895cd7be,$6b901122, $fd987193, $a679438e, $49b40821,$f61e2562, $c040b340, $265e5a51, $e9b6c7aa,$d62f105d, $02441453, $d8a1e681, $e7d3fbc8,$21e1cde6, $c33707d6, $f4d50d87, $455a14ed,$a9e3e905, $fcefa3f8, $676f02d9, $8d2a4c8a,$fffa3942, $8771f681, $6d9d6122, $fde5380c,$a4beea44, $4bdecfa9, $f6bb4b60, $bebfbc70,$289b7ec6, $eaa127fa, $d4ef3085, $04881d05,$d9d4d039, $e6db99e5, $1fa27cf8, $c4ac5665,$f4292244, $432aff97, $ab9423a7, $fc93a039,$655b59c3, $8f0ccc92, $ffeff47d, $85845dd1,$6fa87e4f, $fe2ce6e0, $a3014314, $4e0811a1,$f7537e82, $bd3af235, $2ad7d2bb, $eb86d391]

		Return K
	End

	Method InitShiftTable:Int[]()
		Shift = New Int[64]

		For Local I:= 0 Until 16 Step 4
			Shift[I] = 7
			Shift[I+1] = Shift[I]+5
			Shift[I+2] = Shift[I+1]+5
			Shift[I+3] = Shift[I+2]+5
		Next

		For Local I:= 16 Until 32 Step 4
			Shift[I] = 5
			Shift[I+1] = ((Shift[I]+Shift[I])-1)
			Shift[I+2] = Shift[I+1] + Shift[I]
			Shift[I+3] = Shift[I+2] + Shift[I] + 1
		Next

		For Local I:= 32 Until 48 Step 4
			Shift[I] = 4
			Shift[I+1] = Shift[I]+7
			Shift[I+2] = Shift[I+1]+Shift[I]+1
			Shift[I+3] = (Shift[I+2]+(Shift[I]*2)-1)
		Next

		For Local I:= 48 Until 64 Step 4
			Shift[I] = 6
			Shift[I+1] = Shift[I]+4
			Shift[I+2] = Shift[I+1]+(Shift[I]-1)
			Shift[I+3] = Shift[I+2]+Shift[I]
		Next

		Return Shift
	End

	' Fields:
	Field Shift:Int[], K:Int[]
End

Class MD5Component
	' Global variable(s):
	Global Standard_MD5DataData:MD5Data

	' Functions:
	Function Init:Void()
		If (Standard_MD5DataData <> Null) Then
			Return
		Endif

		Standard_MD5DataData = New MD5Data(True, True)

		Return
	End

	' This isn't recommended:
	Function DeInit:Void()
		If (Standard_MD5DataData = Null) Then
			Return
		Endif

		Standard_MD5DataData.Free()

		Standard_MD5DataData = Null

		Return
	End
End

Class MD5BufferHasher Extends MD5Engine<DataBuffer> Final
	' Constant variable(s):

	' Defaults:

	' Booleans / Flags:
	Const Default_ByteFix:Bool = True ' False
	Const Default_ClearCacheWhenFinished:Bool = True

	' Global variable(s) (Public):
	' Nothing so far.

	' Global variable(s) (Private):
	Private

	Global _Instance:MD5BufferHasher = Null

	Public

	' Functions:
	Function Instance:MD5BufferHasher()
		If (_Instance = Null) Then
			_Instance = New MD5BufferHasher()
		Endif

		Return _Instance
	End

	Function Run:String(Data:DataBuffer)
		Return Instance().Execute(Data)
	End

	' Constructor(s):
	Method New(ByteFix:Bool=Default_ByteFix, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Super.New(ClearCacheWhenFinished)

		Self.ByteFix = ByteFix
	End

	Method New(MetaData:MD5Data, ByteFix:Bool=Default_ByteFix, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Super.New(MetaData, ClearCacheWhenFinished)

		Self.ByteFix = ByteFix
	End

	' Methods:
	Method ExtractData:Int(Message:DataBuffer, Index:Int, Length:Int=AUTO)
		Return Message.PeekByte(Index)
	End

	Method RetrieveLength:Int(Message:DataBuffer)
		Return Message.Length()
	End

	Method CorrectByte:Int(B:Int)
		If (ByteFix) Then
			If (B < 0) Then
				Return (256+B)
			Endif
		Endif

		Return B
	End

	' Fields:

	' Booleans / Flags:
	Field ByteFix:Bool
End

Class MD5StreamHasher Extends MD5Engine<Stream> Final ' This may end up inheriting from 'MD5Hasher' in the future.
	' Constant variable(s):

	' Defaults:
	Const Default_CacheSize:Int = AUTO ' 2048
	Const Default_CharacterEncoding:String = "ascii"

	' Booleans / Flags:
	Const Default_ByteFix:Bool = True
	Const Default_ClearCacheWhenFinished:Bool = True ' False

	' Global variable(s) (Public):

	#Rem
		ATTENTION: For obvious reasons, the 'AUTO_CACHE_DIVISOR' variable must never be set to zero.
	#End

	#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_STRING
		Global MAX_AUTO_CACHE_SIZE:Int = 40*1024*1024 ' 4*1024*1024
		Global AUTO_CACHE_DIVISOR:Int = 16
	#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
		Global MAX_AUTO_CACHE_SIZE:Int = 40*1024*1024
		Global AUTO_CACHE_DIVISOR:Int = 8 ' 16
	#Else
		Global MAX_AUTO_CACHE_SIZE:Int = 40*1024*1024 ' 1*1024*1024
		Global AUTO_CACHE_DIVISOR:Int = 16
	#End

	Global MIN_AUTO_CACHE_SIZE:Int = 8*1024*1024 ' 8*1024

	Global AUTO_CACHE_MAX_SIZE_ENABLED:Bool = True
	Global AUTO_CACHE_MIN_SIZE_ENABLED:Bool = True

	' Defaults:

	' Booleans / Flags:
	Global Default_BulkLoad:Bool = True

	' Global variable(s) (Private):
	Private

	Global _Instance:MD5StreamHasher = Null

	Public

	' Functions:
	Function Instance:MD5StreamHasher()
		If (_Instance = Null) Then
			_Instance = New MD5StreamHasher()
		Endif

		Return _Instance
	End

	Function Run:String(Data:Stream)
		Return Instance().Execute(Data)
	End

	' Constructor(s):
	Method New(CacheSize:Int=Default_CacheSize, BulkLoad:Bool=Default_BulkLoad, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished, ByteFix:Bool=Default_ByteFix)
		Super.New(ClearCacheWhenFinished)

		Self.CacheSize = CacheSize
		Self.BulkLoad = BulkLoad
		Self.ByteFix = ByteFix
	End

	Method New(MetaData:MD5Data, CacheSize:Int=Default_CacheSize, BulkLoad:Bool=Default_BulkLoad, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished, ByteFix:Bool=Default_ByteFix)
		Super.New(MetaData, ClearCacheWhenFinished)

		Self.CacheSize = CacheSize
		Self.BulkLoad = BulkLoad
		Self.ByteFix = ByteFix
	End

	' Methods:
	Method ExtractData:Int(Message:Stream, Index:Int, Length:Int=AUTO)
		' Check for errors:
		#Rem
			If (Message.Eof()) Then
				Return -1
			Endif
		#End

		If (CacheSize = 0) Then
			Return ExtractByte(Message, Index)
		Endif

		Local CacheCheck:Bool = False

		#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
			CacheCheck = (Cache = Null)
		#End

		If (Not CacheCheck) Then
			CacheCheck = (Index >= CachePosition+Cache.Length())
		Endif

		If (CacheCheck) Then
			If (Length = AUTO) Then
				Length = Message.Length()
			Endif

			Local BytesLeft:= Max(Length-Message.Position(), 0)

			Local CacheLengthComparison:Bool = False

			#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
				CacheLengthComparison = (Cache = Null)
			#End

			If (Not CacheLengthComparison) Then
				CacheLengthComparison = (CacheSize <> Cache.Length())
			Endif

			If (CacheLengthComparison) Then
				If (CacheSize = AUTO) Then
					CacheSize = BytesLeft / AUTO_CACHE_DIVISOR

					If (AUTO_CACHE_MAX_SIZE_ENABLED) Then
						CacheSize = Min(CacheSize, MAX_AUTO_CACHE_SIZE)
					Endif

					If (AUTO_CACHE_MIN_SIZE_ENABLED) Then
						CacheSize = Max(CacheSize, MIN_AUTO_CACHE_SIZE)
					Endif
				Endif

				#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY
					If (CacheSize <= 0 And Default_CacheSize > 0) Then
						Cache = New Int[Default_CacheSize]
					Else
						Cache = New Int[CacheSize]
					Endif
				#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
					If (Cache <> Null) Then
						Cache.Discard()
					Endif

					If (CacheSize <= 0 And Default_CacheSize > 0) Then
						Cache = New DataBuffer(Default_CacheSize)
					Else
						Cache = New DataBuffer(CacheSize)
					Endif
				#Else
					' Nothing so far.
				#End
			Else
				'GenericUtilities<Int>.Zero(Cache)
			Endif

			CachePosition = Message.Position()

			Local BytesToRead:= Min(CacheSize, BytesLeft)

			#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY
				If (BulkLoad) Then
					GenericUtilities<Int>.CopyStringToArray(Message.ReadString(BytesToRead, Default_CharacterEncoding), Cache)
				Else
					For Local I:Int = 0 Until BytesToRead
						Cache[I] = ExtractByte(Message, Index+I)
					Next
				Endif
			#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
				If (BulkLoad) Then
					Message.Read(Cache, 0, BytesToRead)
				Else
					For Local I:Int = 0 Until BytesToRead
						Cache.PokeByte(I, ExtractByte(Message, Index+I))
					Next
				Endif
			#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_STRING
				Cache = Message.ReadString(BytesToRead, Default_CharacterEncoding)
			#End
		Endif

		#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
			Return Cache.PeekByte(Index-CachePosition)
		#Else
			Return Cache[Index-CachePosition]
		#End
	End

	Method ExtractByte:Int(Message:Stream, Index:Int)
		Return Message.ReadByte()

		#Rem
			'Local Position:= Message.Position()
			Local Data:Int

			'Message.Seek(Index)

			Data = Message.ReadByte()

			'Message.Seek(Position)

			Return Data
		#End
	End

	Method RetrieveLength:Int(Message:Stream)
		Return Message.Length() ' AUTO
	End

	Method CorrectByte:Int(B:Int)
		If (ByteFix) Then
			If (B < 0) Then
				Return (256+B)
			Endif
		Endif

		Return B
	End

	Method DiscardCache:Void()
		#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY
			Cache = []
		#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_STRING
			Cache = ""
		#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
			Cache.Discard(); Cache = Null
		#End

		Return
	End

	' Fields:
	Field CacheSize:Int
	Field CachePosition:Int

	#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_STRING
		Field Cache:String
	#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
		Field Cache:DataBuffer
	#Else 'Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY
		Field Cache:Int[]
	#End

	' Booleans / Flags:
	Field ByteFix:Bool
	Field BulkLoad:Bool
End

#Rem
	This class is mainly targeted towards arrays (Integer arrays mainly, but 'Object' based arrays would work if 'ToInt' is implemented).
	In addition, 'Strings' and objects which implement 'ToString' should also work.
#End

Class MD5Hasher<T> Extends MD5Engine<T> Final
	' Constant variable(s):

	' Defaults:

	' Booleans / Flags:
	Const Default_ClearCacheWhenFinished:Bool = False

	' Global variable(s) (Public):
	' Nothing so far.

	' Global variable(s) (Private):
	Private

	Global _Instance:MD5Hasher<T> = Null

	Public

	' Functions:
	Function Instance:MD5Hasher<T>()
		If (_Instance = Null) Then
			_Instance = New MD5Hasher<T>()
		Endif

		Return _Instance
	End

	Function Run:String(Data:T)
		Return Instance().Execute(Data)
	End

	' Constructor(s):
	Method New(ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Super.New(ClearCacheWhenFinished)
	End

	Method New(MetaData:MD5Data, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Super.New(MetaData, ClearCacheWhenFinished)
	End

	' Methods:
	Method ExtractData:Int(Message:T, Index:Int, Length:Int=AUTO)
		Return Message[Index]
	End

	Method RetrieveLength:Int(Message:T)
		Return Message.Length()
	End
End

Class MD5Engine<T> Extends MD5Component Abstract
	' Constant variable(s):
	Const BLOCK_SIZE:Int = 16
	Const AUTO:Int = -1

	' Defaults:
	Const Default_Offset:Int = 0

	' Booleans / Flags:
	Const Default_ClearCacheWhenFinished:Bool = False ' True

	' Constructor(s):
	Method New(ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Init()

		Self.MetaData = Standard_MD5DataData
		Self.ClearCacheWhenFinished = ClearCacheWhenFinished
	End

	Method New(MetaData:MD5Data, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Self.MetaData = MetaData
		Self.ClearCacheWhenFinished = ClearCacheWhenFinished
	End

	' Methods (Public):
	Method Execute:String(Message:T, Length:Int=AUTO, Offset:Int=Default_Offset)
		If (Length = AUTO) Then
			Length = RetrieveLength(Message)
		Endif

		' Local variable(s):
		Local BlockCount:Int = ((Length + 8) Shr 6) + 1

		Local A0:Int = $67452301 ' 1732584193
		Local B0:Int = $efcdab89 ' -271733879
		Local C0:Int = $98badcfe ' -1732584194
		Local D0:Int = $10325476 ' 271733878

		Local VirtualBlockArraySize:= (BlockCount * BLOCK_SIZE)
		Local BlockCalculationScope:Int = (BLOCK_SIZE Shl 2)

		For Local BlockIndex:= 0 Until VirtualBlockArraySize Step BLOCK_SIZE
			Local BlockID:Int = 0

			If (BlockIndex > 0) Then
				BlockID = (BlockIndex/BLOCK_SIZE)
			Endif

			GenericUtilities<Int>.Zero(Block)

			For Local I:Int = BlockCalculationScope*BlockID Until Min(BlockCalculationScope*(BlockID+1), Length)
				'(I+Offset) Mod Length
				Block[((I Shr 2) Mod BLOCK_SIZE)] |= (CorrectByte(ExtractData(Message, (I+Offset), Length)) Shl ((I Mod 4) * 8))
			Next

			If (BlockIndex = VirtualBlockArraySize - (BLOCK_SIZE)) Then
				Block[(Length Shr 2) Mod BLOCK_SIZE] |= (128 Shl ((Length Mod 4) * 8))
				Block[(VirtualBlockArraySize - 2) Mod BLOCK_SIZE] = Length * 8
			Endif

			' Local variable(s):
			Local A:= A0
			Local B:= B0
			Local C:= C0
			Local D:= D0

			Local F:Int
			Local G:Int

			For Local I:= 0 Until 64
				If (I < 16) Then
					F = (B & C) | ((~B) & D)
					G = I
				Elseif (I < 32) Then
					F = (D & B) | ((~D) & C)
					G = (5*I + 1) Mod 16
				Elseif (I < 48) Then
					F = B ~ C ~ D
					G = (3*I + 5) Mod 16
				Elseif (I < 64) Then
					F = C ~ (B | (~D))
					G = (7*I) Mod 16
				Endif

				Local TempD:= D

				D = C
				C = B
				B += RotateLeft(A + F + MetaData.K[I] + Block[G Mod BLOCK_SIZE], MetaData.Shift[I])
				A = TempD
			Next

			A0 += A
			B0 += B
			C0 += C
			D0 += D
		Next

		If (ClearCacheWhenFinished) Then
			DiscardCache()
		Endif

		Return HexLE((A0)) + HexLE((B0)) + HexLE((C0)) + HexLE((D0))
	End

	Method CorrectByte:Int(B:Int)
		Return B
	End

	' Purely virtual methods:
	Method DiscardCache:Void()
		' Implement this if needed.

		Return
	End

	' Abstract methods:
	Method ExtractData:Int(Message:T, Index:Int, Length:Int=AUTO) Abstract
	Method RetrieveLength:Int(Message:T) Abstract

	' Methods (Private):
	Private

	' Nothing so far.

	Public

	' Fields:

	' Most MD5 related meta-data needed for execution.
	Field MetaData:MD5Data

	' An array cached per-hasher, which is used for block calculations upon execution.
	Field Block:Int[BLOCK_SIZE]

	' Booleans / Flags:
	Field ClearCacheWhenFinished:Bool
End

#Rem
	NOTES:
		* Please do not use the standard 'MD5Hasher' class for 'DataBuffers' and 'Streams'.
		These must be handled by their respective "hashers". Other than that, you should be good.

		* The 'MD5Hasher' class was developed with arrays (Integer arrays) in mind, but by extension, 'Strings' do work. (Very well, I might add)

		* To use this module simply do the following:

		Rough example:

		MD5HasherClassHere.Run(DataHere)

		Real world example:

		' To calculate the hash of a string, do this.
		MD5Hasher<String>.Run(Str)

		Alternatively, you can call 'MD5HasherClassHere.Instance' in order to configure the global instance.
		Or, you could always create your own instance of the class, and configure it from there.

		For the sake of benchmarking, it's recommended that you just use the global instance as you see fit. (Through 'Run', or 'Instance')

		* Standard 'Stream' support is implemented, but not completely tested.
		It's a dirty hack, but it does the job well enough. Use at your own risk.

		* All functionality present should be in working order, and follow standard MD5 behaviour.
		If you get undesired results, please let me know. Please note that on the standard C++ targets (Targets using Monkey's garbage collector),
		this implementation tends to be bottlenecked by your GC settings.

		Some things are generated for the sake of memory efficiency. This was built with limited memory environments in mind.
		In fact part of why I made this was to test out Monkey performance-wise on embedded ARM systems.

		Most things which are generated are specific to the input, and because of this, they can not be cached properly.
		(Without some reworking which I may or may not do)
#End

Strict

Public

' Preprocessor related:
#MD5_STREAM_CACHE_MODE_ARRAY		= 0
#MD5_STREAM_CACHE_MODE_STRING		= 1
#MD5_STREAM_CACHE_MODE_BUFFER		= 2

#If TARGET = "android"
	#MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY ' MD5_STREAM_CACHE_MODE_STRING
#Else
	#MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY ' MD5_STREAM_CACHE_MODE_BUFFER
#End

' Imports (External):
Import util
Import sizeof

' Standard BRL modules:
Import brl.stream
Import brl.databuffer

' Imports (Internal):

' External language bindings.
Import external

' Global variable(s):
' Nothing so far.

' Functions:

' This is effectively just a really quick rewrite of Java's 'hashCode' method:
Function HashCode:Int(S:String)
	' Local variable(s):
	Local Hash:Int = 0

	' Compute the "hash code":
	For Local I:= 0 Until S.Length()
		Hash = 31*Hash + S[I]
	Next

	' Return the resulting hash.
	Return Hash
End

Function RotateLeft:Int(Value:Int, ShiftBits:Int)
	Return Lsl(Value, ShiftBits) | Lsr( Value, (SizeOf_Integer_InBits - ShiftBits))
	'Return (Value Shl ShiftBits) | (Value Shr (32-ShiftBits))
End

' Classes:
Class MD5Data
	' Constant variable(s):

	' Defaults:

	' Booleans / Flags:
	Const Default_InitKTable:Bool = True
	Const Default_InitShiftTable:Bool = True

	' Global variable(s):
	' Nothing so far.

	' Functions:
	' Nothing so far.

	' Constructor(s):
	Method New(MakeKTable:Bool=Default_InitKTable, MakeShiftTable:Bool=Default_InitShiftTable)
		If (MakeKTable) Then
			InitKTable()
		Endif

		If (MakeShiftTable) Then
			InitShiftTable()
		Endif
	End

	' Destructor(s):
	Method Free:Bool()
		K = []
		Shift = []

		' Return the default response.
		Return True
	End

	' Methods:
	Method InitKTable:Int[]()
		'K = [-680876936,-389564586,606105819,-1044525330,-176418897,1200080426,-1473231341,-45705983,1770035416,-1958414417,-42063,-1990404162,1804603682,-40341101,-1502002290,1236535329,-165796510,-1069501632,643717713,-373897302,-701558691,38016083,-660478335,-405537848,568446438,-1019803690,-187363961,1163531501,-1444681467,-51403784,1735328473,-1926607734,-378558,-2022574463,1839030562,-35309556,-1530992060,1272893353,-155497632,-1094730640,681279174,-358537222,-722521979,76029189,-640364487,-421815835,530742520,-995338651,-198630844,1126891415,-1416354905,-57434055,1700485571,-1894986606,-1051523,-2054922799,1873313359,-30611744,-1560198380,1309151649,-145523070,-1120210379,718787259,-343485551]
		K = [$d76aa478, $e8c7b756, $242070db, $c1bdceee, $f57c0faf, $4787c62a, $a8304613, $fd469501, $698098d8, $8b44f7af, $ffff5bb1, $895cd7be,$6b901122, $fd987193, $a679438e, $49b40821,$f61e2562, $c040b340, $265e5a51, $e9b6c7aa,$d62f105d, $02441453, $d8a1e681, $e7d3fbc8,$21e1cde6, $c33707d6, $f4d50d87, $455a14ed,$a9e3e905, $fcefa3f8, $676f02d9, $8d2a4c8a,$fffa3942, $8771f681, $6d9d6122, $fde5380c,$a4beea44, $4bdecfa9, $f6bb4b60, $bebfbc70,$289b7ec6, $eaa127fa, $d4ef3085, $04881d05,$d9d4d039, $e6db99e5, $1fa27cf8, $c4ac5665,$f4292244, $432aff97, $ab9423a7, $fc93a039,$655b59c3, $8f0ccc92, $ffeff47d, $85845dd1,$6fa87e4f, $fe2ce6e0, $a3014314, $4e0811a1,$f7537e82, $bd3af235, $2ad7d2bb, $eb86d391]

		Return K
	End

	Method InitShiftTable:Int[]()
		Shift = New Int[64]

		For Local I:= 0 Until 16 Step 4
			Shift[I] = 7
			Shift[I+1] = Shift[I]+5
			Shift[I+2] = Shift[I+1]+5
			Shift[I+3] = Shift[I+2]+5
		Next

		For Local I:= 16 Until 32 Step 4
			Shift[I] = 5
			Shift[I+1] = ((Shift[I]+Shift[I])-1)
			Shift[I+2] = Shift[I+1] + Shift[I]
			Shift[I+3] = Shift[I+2] + Shift[I] + 1
		Next

		For Local I:= 32 Until 48 Step 4
			Shift[I] = 4
			Shift[I+1] = Shift[I]+7
			Shift[I+2] = Shift[I+1]+Shift[I]+1
			Shift[I+3] = (Shift[I+2]+(Shift[I]*2)-1)
		Next

		For Local I:= 48 Until 64 Step 4
			Shift[I] = 6
			Shift[I+1] = Shift[I]+4
			Shift[I+2] = Shift[I+1]+(Shift[I]-1)
			Shift[I+3] = Shift[I+2]+Shift[I]
		Next

		Return Shift
	End

	' Fields:
	Field Shift:Int[], K:Int[]
End

Class MD5Component
	' Global variable(s):
	Global Standard_MD5DataData:MD5Data

	' Functions:
	Function Init:Void()
		If (Standard_MD5DataData <> Null) Then
			Return
		Endif

		Standard_MD5DataData = New MD5Data(True, True)

		Return
	End

	' This isn't recommended:
	Function DeInit:Void()
		If (Standard_MD5DataData = Null) Then
			Return
		Endif

		Standard_MD5DataData.Free()

		Standard_MD5DataData = Null

		Return
	End
End

Class MD5BufferHasher Extends MD5Engine<DataBuffer> Final
	' Constant variable(s):

	' Defaults:

	' Booleans / Flags:
	Const Default_ByteFix:Bool = True ' False
	Const Default_ClearCacheWhenFinished:Bool = True

	' Global variable(s) (Public):
	' Nothing so far.

	' Global variable(s) (Private):
	Private

	Global _Instance:MD5BufferHasher = Null

	Public

	' Functions:
	Function Instance:MD5BufferHasher()
		If (_Instance = Null) Then
			_Instance = New MD5BufferHasher()
		Endif

		Return _Instance
	End

	Function Run:String(Data:DataBuffer)
		Return Instance().Execute(Data)
	End

	' Constructor(s):
	Method New(ByteFix:Bool=Default_ByteFix, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Super.New(ClearCacheWhenFinished)

		Self.ByteFix = ByteFix
	End

	Method New(MetaData:MD5Data, ByteFix:Bool=Default_ByteFix, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Super.New(MetaData, ClearCacheWhenFinished)

		Self.ByteFix = ByteFix
	End

	' Methods:
	Method ExtractData:Int(Message:DataBuffer, Index:Int, Length:Int=AUTO)
		Return Message.PeekByte(Index)
	End

	Method RetrieveLength:Int(Message:DataBuffer)
		Return Message.Length()
	End

	Method CorrectByte:Int(B:Int)
		If (ByteFix) Then
			If (B < 0) Then
				Return (256+B)
			Endif
		Endif

		Return B
	End

	' Fields:

	' Booleans / Flags:
	Field ByteFix:Bool
End

Class MD5StreamHasher Extends MD5Engine<Stream> Final ' This may end up inheriting from 'MD5Hasher' in the future.
	' Constant variable(s):

	' Defaults:
	Const Default_CacheSize:Int = AUTO ' 2048
	Const Default_CharacterEncoding:String = "ascii"

	' Booleans / Flags:
	Const Default_ByteFix:Bool = True
	Const Default_ClearCacheWhenFinished:Bool = True ' False

	' Global variable(s) (Public):

	#Rem
		ATTENTION: For obvious reasons, the 'AUTO_CACHE_DIVISOR' variable must never be set to zero.
	#End

	#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_STRING
		Global MAX_AUTO_CACHE_SIZE:Int = 40*1024*1024 ' 4*1024*1024
		Global AUTO_CACHE_DIVISOR:Int = 16
	#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
		Global MAX_AUTO_CACHE_SIZE:Int = 40*1024*1024
		Global AUTO_CACHE_DIVISOR:Int = 8 ' 16
	#Else
		Global MAX_AUTO_CACHE_SIZE:Int = 40*1024*1024 ' 1*1024*1024
		Global AUTO_CACHE_DIVISOR:Int = 16
	#End

	Global MIN_AUTO_CACHE_SIZE:Int = 8*1024*1024 ' 8*1024

	Global AUTO_CACHE_MAX_SIZE_ENABLED:Bool = True
	Global AUTO_CACHE_MIN_SIZE_ENABLED:Bool = True

	' Defaults:

	' Booleans / Flags:
	Global Default_BulkLoad:Bool = True

	' Global variable(s) (Private):
	Private

	Global _Instance:MD5StreamHasher = Null

	Public

	' Functions:
	Function Instance:MD5StreamHasher()
		If (_Instance = Null) Then
			_Instance = New MD5StreamHasher()
		Endif

		Return _Instance
	End

	Function Run:String(Data:Stream)
		Return Instance().Execute(Data)
	End

	' Constructor(s):
	Method New(CacheSize:Int=Default_CacheSize, BulkLoad:Bool=Default_BulkLoad, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished, ByteFix:Bool=Default_ByteFix)
		Super.New(ClearCacheWhenFinished)

		Self.CacheSize = CacheSize
		Self.BulkLoad = BulkLoad
		Self.ByteFix = ByteFix
	End

	Method New(MetaData:MD5Data, CacheSize:Int=Default_CacheSize, BulkLoad:Bool=Default_BulkLoad, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished, ByteFix:Bool=Default_ByteFix)
		Super.New(MetaData, ClearCacheWhenFinished)

		Self.CacheSize = CacheSize
		Self.BulkLoad = BulkLoad
		Self.ByteFix = ByteFix
	End

	' Methods:
	Method ExtractData:Int(Message:Stream, Index:Int, Length:Int=AUTO)
		' Check for errors:
		#Rem
			If (Message.Eof()) Then
				Return -1
			Endif
		#End

		If (CacheSize = 0) Then
			Return ExtractByte(Message, Index)
		Endif

		Local CacheCheck:Bool = False

		#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
			CacheCheck = (Cache = Null)
		#End

		If (Not CacheCheck) Then
			CacheCheck = (Index >= CachePosition+Cache.Length())
		Endif

		If (CacheCheck) Then
			If (Length = AUTO) Then
				Length = Message.Length()
			Endif

			Local BytesLeft:= Max(Length-Message.Position(), 0)

			Local CacheLengthComparison:Bool = False

			#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
				CacheLengthComparison = (Cache = Null)
			#End

			If (Not CacheLengthComparison) Then
				CacheLengthComparison = (CacheSize <> Cache.Length())
			Endif

			If (CacheLengthComparison) Then
				If (CacheSize = AUTO) Then
					CacheSize = BytesLeft / AUTO_CACHE_DIVISOR

					If (AUTO_CACHE_MAX_SIZE_ENABLED) Then
						CacheSize = Min(CacheSize, MAX_AUTO_CACHE_SIZE)
					Endif

					If (AUTO_CACHE_MIN_SIZE_ENABLED) Then
						CacheSize = Max(CacheSize, MIN_AUTO_CACHE_SIZE)
					Endif
				Endif

				#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY
					If (CacheSize <= 0 And Default_CacheSize > 0) Then
						Cache = New Int[Default_CacheSize]
					Else
						Cache = New Int[CacheSize]
					Endif
				#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
					If (Cache <> Null) Then
						Cache.Discard()
					Endif

					If (CacheSize <= 0 And Default_CacheSize > 0) Then
						Cache = New DataBuffer(Default_CacheSize)
					Else
						Cache = New DataBuffer(CacheSize)
					Endif
				#Else
					' Nothing so far.
				#End
			Else
				'GenericUtilities<Int>.Zero(Cache)
			Endif

			CachePosition = Message.Position()

			Local BytesToRead:= Min(CacheSize, BytesLeft)

			#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY
				If (BulkLoad) Then
					GenericUtilities<Int>.CopyStringToArray(Message.ReadString(BytesToRead, Default_CharacterEncoding), Cache)
				Else
					For Local I:Int = 0 Until BytesToRead
						Cache[I] = ExtractByte(Message, Index+I)
					Next
				Endif
			#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
				If (BulkLoad) Then
					Message.Read(Cache, 0, BytesToRead)
				Else
					For Local I:Int = 0 Until BytesToRead
						Cache.PokeByte(I, ExtractByte(Message, Index+I))
					Next
				Endif
			#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_STRING
				Cache = Message.ReadString(BytesToRead, Default_CharacterEncoding)
			#End
		Endif

		#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
			Return Cache.PeekByte(Index-CachePosition)
		#Else
			Return Cache[Index-CachePosition]
		#End
	End

	Method ExtractByte:Int(Message:Stream, Index:Int)
		Return Message.ReadByte()

		#Rem
			'Local Position:= Message.Position()
			Local Data:Int

			'Message.Seek(Index)

			Data = Message.ReadByte()

			'Message.Seek(Position)

			Return Data
		#End
	End

	Method RetrieveLength:Int(Message:Stream)
		Return Message.Length() ' AUTO
	End

	Method CorrectByte:Int(B:Int)
		If (ByteFix) Then
			If (B < 0) Then
				Return (256+B)
			Endif
		Endif

		Return B
	End

	Method DiscardCache:Void()
		#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY
			Cache = []
		#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_STRING
			Cache = ""
		#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
			Cache.Discard(); Cache = Null
		#End

		Return
	End

	' Fields:
	Field CacheSize:Int
	Field CachePosition:Int

	#If MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_STRING
		Field Cache:String
	#Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_BUFFER
		Field Cache:DataBuffer
	#Else 'Elseif MD5_STREAM_CACHE_MODE = MD5_STREAM_CACHE_MODE_ARRAY
		Field Cache:Int[]
	#End

	' Booleans / Flags:
	Field ByteFix:Bool
	Field BulkLoad:Bool
End

#Rem
	This class is mainly targeted towards arrays (Integer arrays mainly, but 'Object' based arrays would work if 'ToInt' is implemented).
	In addition, 'Strings' and objects which implement 'ToString' should also work.
#End

Class MD5Hasher<T> Extends MD5Engine<T> Final
	' Constant variable(s):

	' Defaults:

	' Booleans / Flags:
	Const Default_ClearCacheWhenFinished:Bool = False

	' Global variable(s) (Public):
	' Nothing so far.

	' Global variable(s) (Private):
	Private

	Global _Instance:MD5Hasher<T> = Null

	Public

	' Functions:
	Function Instance:MD5Hasher<T>()
		If (_Instance = Null) Then
			_Instance = New MD5Hasher<T>()
		Endif

		Return _Instance
	End

	Function Run:String(Data:T)
		Return Instance().Execute(Data)
	End

	' Constructor(s):
	Method New(ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Super.New(ClearCacheWhenFinished)
	End

	Method New(MetaData:MD5Data, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Super.New(MetaData, ClearCacheWhenFinished)
	End

	' Methods:
	Method ExtractData:Int(Message:T, Index:Int, Length:Int=AUTO)
		Return Message[Index]
	End

	Method RetrieveLength:Int(Message:T)
		Return Message.Length()
	End
End

Class MD5Engine<T> Extends MD5Component Abstract
	' Constant variable(s):
	Const BLOCK_SIZE:Int = 16
	Const AUTO:Int = -1

	' Defaults:
	Const Default_Offset:Int = 0

	' Booleans / Flags:
	Const Default_ClearCacheWhenFinished:Bool = False ' True

	' Constructor(s):
	Method New(ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Init()

		Self.MetaData = Standard_MD5DataData
		Self.ClearCacheWhenFinished = ClearCacheWhenFinished
	End

	Method New(MetaData:MD5Data, ClearCacheWhenFinished:Bool=Default_ClearCacheWhenFinished)
		Self.MetaData = MetaData
		Self.ClearCacheWhenFinished = ClearCacheWhenFinished
	End

	' Methods (Public):
	Method Execute:String(Message:T, Length:Int=AUTO, Offset:Int=Default_Offset)
		If (Length = AUTO) Then
			Length = RetrieveLength(Message)
		Endif

		' Local variable(s):
		Local BlockCount:Int = ((Length + 8) Shr 6) + 1

		Local A0:Int = $67452301 ' 1732584193
		Local B0:Int = $efcdab89 ' -271733879
		Local C0:Int = $98badcfe ' -1732584194
		Local D0:Int = $10325476 ' 271733878

		Local VirtualBlockArraySize:= (BlockCount * BLOCK_SIZE)
		Local BlockCalculationScope:Int = (BLOCK_SIZE Shl 2)

		For Local BlockIndex:= 0 Until VirtualBlockArraySize Step BLOCK_SIZE
			Local BlockID:Int = 0

			If (BlockIndex > 0) Then
				BlockID = (BlockIndex/BLOCK_SIZE)
			Endif

			GenericUtilities<Int>.Zero(Block)

			For Local I:Int = BlockCalculationScope*BlockID Until Min(BlockCalculationScope*(BlockID+1), Length)
				'(I+Offset) Mod Length
				Block[((I Shr 2) Mod BLOCK_SIZE)] |= (CorrectByte(ExtractData(Message, (I+Offset), Length)) Shl ((I Mod 4) * 8))
			Next

			If (BlockIndex = VirtualBlockArraySize - (BLOCK_SIZE)) Then
				Block[(Length Shr 2) Mod BLOCK_SIZE] |= (128 Shl ((Length Mod 4) * 8))
				Block[(VirtualBlockArraySize - 2) Mod BLOCK_SIZE] = Length * 8
			Endif

			' Local variable(s):
			Local A:= A0
			Local B:= B0
			Local C:= C0
			Local D:= D0

			Local F:Int
			Local G:Int

			For Local I:= 0 Until 64
				If (I < 16) Then
					F = (B & C) | ((~B) & D)
					G = I
				Elseif (I < 32) Then
					F = (D & B) | ((~D) & C)
					G = (5*I + 1) Mod 16
				Elseif (I < 48) Then
					F = B ~ C ~ D
					G = (3*I + 5) Mod 16
				Elseif (I < 64) Then
					F = C ~ (B | (~D))
					G = (7*I) Mod 16
				Endif

				Local TempD:= D

				D = C
				C = B
				B += RotateLeft(A + F + MetaData.K[I] + Block[G Mod BLOCK_SIZE], MetaData.Shift[I])
				A = TempD
			Next

			A0 += A
			B0 += B
			C0 += C
			D0 += D
		Next

		If (ClearCacheWhenFinished) Then
			DiscardCache()
		Endif

		Return HexLE((A0)) + HexLE((B0)) + HexLE((C0)) + HexLE((D0))
	End

	Method CorrectByte:Int(B:Int)
		Return B
	End

	' Purely virtual methods:
	Method DiscardCache:Void()
		' Implement this if needed.

		Return
	End

	' Abstract methods:
	Method ExtractData:Int(Message:T, Index:Int, Length:Int=AUTO) Abstract
	Method RetrieveLength:Int(Message:T) Abstract

	' Methods (Private):
	Private

	' Nothing so far.

	Public

	' Fields:

	' Most MD5 related meta-data needed for execution.
	Field MetaData:MD5Data

	' An array cached per-hasher, which is used for block calculations upon execution.
	Field Block:Int[BLOCK_SIZE]

	' Booleans / Flags:
	Field ClearCacheWhenFinished:Bool
End


add parentalgate to main import file

add parentalgate to main import file

Strict

#REFLECTION_FILTER+="*test|*src.tests*"

Public

Import bono.src.exceptions.exception
Import bono.src.exceptions.invalidargumentexception
Import bono.src.exceptions.invalidconstructorexception
Import bono.src.exceptions.invalidxmlexception
Import bono.src.exceptions.notfoundexception
Import bono.src.exceptions.runtimeexception
Import bono.src.fx.actions.action
Import bono.src.fx.actions.actioncallback
Import bono.src.fx.actions.actionfadealphato
Import bono.src.fx.actions.actionmoveto
Import bono.src.fx.actions.actionparallel
Import bono.src.fx.actions.actionrotate
Import bono.src.fx.actions.actionscale
Import bono.src.fx.actions.actionscallback
Import bono.src.fx.actions.actionsizeto
Import bono.src.fx.actions.actionsleep
Import bono.src.fx.actions.actionsleeprandomlateeval
Import bono.src.fx.actions.baseaction
Import bono.src.fx.oldanim.animation
Import bono.src.fx.oldanim.effect
Import bono.src.fx.oldanim.effectcolor
Import bono.src.fx.oldanim.effectcoloralpha
Import bono.src.fx.oldanim.effectreverse
Import bono.src.fx.particle.particle
Import bono.src.fx.particle.particleemitter
Import bono.src.fx.particle.particleemittercircle
Import bono.src.fx.particle.particlesystem
Import bono.src.fx.timeline.timeline
Import bono.src.fx.timeline.timelineable
Import bono.src.fx.timeline.timelinefactory
Import bono.src.fx.transition.transition
Import bono.src.fx.transition.transitionback
Import bono.src.fx.transition.transitionbounce
Import bono.src.fx.transition.transitioncirc
Import bono.src.fx.transition.transitioncubic
Import bono.src.fx.transition.transitionelastic
Import bono.src.fx.transition.transitionexpo
Import bono.src.fx.transition.transitionlinear
Import bono.src.fx.transition.transitionquad
Import bono.src.fx.transition.transitionquart
Import bono.src.fx.transition.transitionrandom
Import bono.src.fx.transition.transitionsine
Import bono.src.graphics.align
Import bono.src.graphics.basedisplayobject
Import bono.src.graphics.clearscreen
Import bono.src.graphics.color.color
Import bono.src.graphics.color.colorable
Import bono.src.graphics.color.colorblend
Import bono.src.graphics.fpscounter
Import bono.src.graphics.interfaces.alignable
Import bono.src.graphics.interfaces.positionable
Import bono.src.graphics.interfaces.rotateable
Import bono.src.graphics.interfaces.sizeable
Import bono.src.graphics.shapes.baseshape
Import bono.src.graphics.shapes.circle
Import bono.src.graphics.shapes.ellipse
Import bono.src.graphics.shapes.line
Import bono.src.graphics.shapes.rect
Import bono.src.graphics.sprite
Import bono.src.graphics.text
Import bono.src.gui.guibase
Import bono.src.gui.guibutton
Import bono.src.gui.guibuttoncallback
Import bono.src.gui.guibuttondowncallback
Import bono.src.gui.guibuttonmovecallback
Import bono.src.gui.guibuttonupcallback
Import bono.src.gui.guicallbackbutton
Import bono.src.helper.arrayhelper
Import bono.src.helper.mathhelper
Import bono.src.helper.matrixhelper
Import bono.src.helper.objecthelper
Import bono.src.helper.stringhelper
Import bono.src.io.assetloader
Import bono.src.io.imageloader.cachedimageloader
Import bono.src.io.imageloader.imageloader
Import bono.src.io.imageloader.mockimageloader
Import bono.src.io.imageloader.mojoimageloader
Import bono.src.io.persistable
Import bono.src.io.statestore
Import bono.src.io.texturepacker
Import bono.src.kernel.app
Import bono.src.kernel.contentscaler.contentscaler
Import bono.src.kernel.contentscaler.contentscalerfull
Import bono.src.kernel.deltatimer
Import bono.src.kernel.director
Import bono.src.kernel.interfaces.renderable
Import bono.src.kernel.interfaces.sceneable
Import bono.src.kernel.interfaces.suspendable
Import bono.src.kernel.interfaces.updateable
Import bono.src.kernel.key.keyemitter
Import bono.src.kernel.key.keyevent
Import bono.src.kernel.key.keyhandler
Import bono.src.kernel.key.keyhandlerfan
Import bono.src.kernel.scene
Import bono.src.kernel.scenetransition.scenetransition
Import bono.src.kernel.scenetransition.scenetransitionflash
Import bono.src.kernel.scenetransition.scenetransitioninstant
Import bono.src.kernel.scenetransition.scenetransitionproxy
Import bono.src.kernel.soundmanager
Import bono.src.kernel.touch.touchable
Import bono.src.kernel.touch.touchablefan
Import bono.src.kernel.touch.touchemitter
Import bono.src.kernel.touch.touchevent
Import bono.src.parentalgate
Import bono.src.tests.apptestcase
Import bono.src.tests.apptestsuite
Import bono.src.tests.assert
Import bono.src.tests.exception.assertionfailedexception
Import bono.src.tests.exception.testfailedexception
Import bono.src.tests.exception.testincompleteexception
Import bono.src.tests.exception.testskippedexception
Import bono.src.tests.simpletestsuite
Import bono.src.tests.testcase
Import bono.src.tests.testlistener
Import bono.src.tests.testreportsimple
Import bono.src.tests.testrunner
Import bono.src.tests.testsuite
Import bono.src.tests.testsuitehelper
Import bono.src.utils.alertcallback
Import bono.src.utils.countdown.countdown
Import bono.src.utils.countdown.countdownfinishcallback
Import bono.src.utils.countdown.countdowntickcallback
Import bono.src.utils.device
Import bono.src.utils.pool
Import bono.src.utils.registry.registry
Import bono.src.utils.registry.registrystore
Import bono.src.utils.registry.registryvaluenotfoundexception
Import bono.src.utils.target
Import bono.src.utils.vector2d

Strict

#REFLECTION_FILTER+="*test|*src.tests*"

Public

Import bono.src.exceptions.exception
Import bono.src.exceptions.invalidargumentexception
Import bono.src.exceptions.invalidconstructorexception
Import bono.src.exceptions.invalidxmlexception
Import bono.src.exceptions.notfoundexception
Import bono.src.exceptions.runtimeexception
Import bono.src.fx.actions.action
Import bono.src.fx.actions.actioncallback
Import bono.src.fx.actions.actionfadealphato
Import bono.src.fx.actions.actionmoveto
Import bono.src.fx.actions.actionparallel
Import bono.src.fx.actions.actionrotate
Import bono.src.fx.actions.actionscale
Import bono.src.fx.actions.actionscallback
Import bono.src.fx.actions.actionsizeto
Import bono.src.fx.actions.actionsleep
Import bono.src.fx.actions.actionsleeprandomlateeval
Import bono.src.fx.actions.baseaction
Import bono.src.fx.oldanim.animation
Import bono.src.fx.oldanim.effect
Import bono.src.fx.oldanim.effectcolor
Import bono.src.fx.oldanim.effectcoloralpha
Import bono.src.fx.oldanim.effectreverse
Import bono.src.fx.particle.particle
Import bono.src.fx.particle.particleemitter
Import bono.src.fx.particle.particleemittercircle
Import bono.src.fx.particle.particlesystem
Import bono.src.fx.timeline.timeline
Import bono.src.fx.timeline.timelineable
Import bono.src.fx.timeline.timelinefactory
Import bono.src.fx.transition.transition
Import bono.src.fx.transition.transitionback
Import bono.src.fx.transition.transitionbounce
Import bono.src.fx.transition.transitioncirc
Import bono.src.fx.transition.transitioncubic
Import bono.src.fx.transition.transitionelastic
Import bono.src.fx.transition.transitionexpo
Import bono.src.fx.transition.transitionlinear
Import bono.src.fx.transition.transitionquad
Import bono.src.fx.transition.transitionquart
Import bono.src.fx.transition.transitionrandom
Import bono.src.fx.transition.transitionsine
Import bono.src.graphics.align
Import bono.src.graphics.basedisplayobject
Import bono.src.graphics.clearscreen
Import bono.src.graphics.color.color
Import bono.src.graphics.color.colorable
Import bono.src.graphics.color.colorblend
Import bono.src.graphics.fpscounter
Import bono.src.graphics.interfaces.alignable
Import bono.src.graphics.interfaces.positionable
Import bono.src.graphics.interfaces.rotateable
Import bono.src.graphics.interfaces.sizeable
Import bono.src.graphics.shapes.baseshape
Import bono.src.graphics.shapes.circle
Import bono.src.graphics.shapes.ellipse
Import bono.src.graphics.shapes.line
Import bono.src.graphics.shapes.rect
Import bono.src.graphics.sprite
Import bono.src.graphics.text
Import bono.src.gui.guibase
Import bono.src.gui.guibutton
Import bono.src.gui.guibuttoncallback
Import bono.src.gui.guibuttondowncallback
Import bono.src.gui.guibuttonmovecallback
Import bono.src.gui.guibuttonupcallback
Import bono.src.gui.guicallbackbutton
Import bono.src.helper.arrayhelper
Import bono.src.helper.mathhelper
Import bono.src.helper.matrixhelper
Import bono.src.helper.objecthelper
Import bono.src.helper.stringhelper
Import bono.src.io.assetloader
Import bono.src.io.imageloader.cachedimageloader
Import bono.src.io.imageloader.imageloader
Import bono.src.io.imageloader.mockimageloader
Import bono.src.io.imageloader.mojoimageloader
Import bono.src.io.persistable
Import bono.src.io.statestore
Import bono.src.io.texturepacker
Import bono.src.kernel.app
Import bono.src.kernel.contentscaler.contentscaler
Import bono.src.kernel.contentscaler.contentscalerfull
Import bono.src.kernel.deltatimer
Import bono.src.kernel.director
Import bono.src.kernel.interfaces.renderable
Import bono.src.kernel.interfaces.sceneable
Import bono.src.kernel.interfaces.suspendable
Import bono.src.kernel.interfaces.updateable
Import bono.src.kernel.key.keyemitter
Import bono.src.kernel.key.keyevent
Import bono.src.kernel.key.keyhandler
Import bono.src.kernel.key.keyhandlerfan
Import bono.src.kernel.scene
Import bono.src.kernel.scenetransition.scenetransition
Import bono.src.kernel.scenetransition.scenetransitionflash
Import bono.src.kernel.scenetransition.scenetransitioninstant
Import bono.src.kernel.scenetransition.scenetransitionproxy
Import bono.src.kernel.soundmanager
Import bono.src.kernel.touch.touchable
Import bono.src.kernel.touch.touchablefan
Import bono.src.kernel.touch.touchemitter
Import bono.src.kernel.touch.touchevent
Import bono.src.tests.apptestcase
Import bono.src.tests.apptestsuite
Import bono.src.tests.assert
Import bono.src.tests.exception.assertionfailedexception
Import bono.src.tests.exception.testfailedexception
Import bono.src.tests.exception.testincompleteexception
Import bono.src.tests.exception.testskippedexception
Import bono.src.tests.simpletestsuite
Import bono.src.tests.testcase
Import bono.src.tests.testlistener
Import bono.src.tests.testreportsimple
Import bono.src.tests.testrunner
Import bono.src.tests.testsuite
Import bono.src.tests.testsuitehelper
Import bono.src.utils.alertcallback
Import bono.src.utils.countdown.countdown
Import bono.src.utils.countdown.countdownfinishcallback
Import bono.src.utils.countdown.countdowntickcallback
Import bono.src.utils.device
Import bono.src.utils.pool
Import bono.src.utils.registry.registry
Import bono.src.utils.registry.registrystore
Import bono.src.utils.registry.registryvaluenotfoundexception
Import bono.src.utils.target
Import bono.src.utils.vector2d


Removed text driver param from button constructor

Strict

Import reflection

Import flxextern
Import flxsprite
Import flxtext
Import flxpoint
Import flxsound
Import flxg
Import flxcamera
Import system.flxassetsmanager

Import "data/button_flx.png"
Import "data/beep_flx.mp3"

Class FlxButton Extends FlxSprite

	Global ClassObject:Object

	Const NORMAL:Int = 0

	Const HIGHLIGHT:Int = 1

	Const PRESSED:Int = 2

	Field label:FlxText

	Field labelOffset:FlxPoint

	Field onUp:FlxButtonClickListener

	Field onDown:FlxButtonDownListener

	Field onOver:FlxButtonOverListener

	Field onOut:FlxButtonOutListener

	Field status:Int

	Field soundOver:FlxSound

	Field soundOut:FlxSound

	Field soundDown:FlxSound

	Field soundUp:FlxSound

Private
	Field _onToggle:Bool

	Field _pressed:Bool

	Field _initialized:Bool

Public
	Method New(x:Float = 0, y:Float = 0, label:String = "", onClick:FlxButtonClickListener = Null)
		Super.New(x, y)

		If (label.Length() > 0) Then
			Self.label = New FlxText(0, 0, 80, label)
			Self.label.SetFormat(FlxText.SYSTEM_FONT, 8, $FF333333, FlxText.ALIGN_CENTER)
			labelOffset = New FlxPoint(-1, 3)
		End If

		LoadGraphic("button" + FlxG.DATA_SUFFIX, True, False, 80, 20)
		onUp = onClick
		onDown = Null
		onOut = Null
		onOver = Null

		soundOver = Null
		soundOut = Null
		soundDown = Null
		soundUp = Null

		status = NORMAL
		_onToggle = False
		_pressed = False
	End Method

	Method Destroy:Void()
		If (label <> Null) Then
			label.Destroy()
			label = Null
		End If

		onUp = Null
		onDown = Null
		onOut = Null
		onOver = Null

		If (soundOver <> Null) Then
			soundOver.Destroy()
			soundOver = Null
		End If

		If (soundOut <> Null) Then
			soundOut.Destroy()
			soundOut = Null
		End If

		If (soundDown <> Null) Then
			soundDown.Destroy()
			soundDown = Null
		End If

		If (soundUp <> Null) Then
			soundUp.Destroy()
			soundUp = Null
		End If

		Super.Destroy()
	End Method

	Method Update:Void()
		_UpdateButton()

		If (label = Null) Return

		Select (Frame)
			Case HIGHLIGHT
				label.Alpha = 1.0

			Case PRESSED
				label.Alpha = .5
				label.y += 1

			Case NORMAL
				label.Alpha = .8

			Default
				label.Alpha = .8
		End Select
	End Method

	Method Draw:Void()
		Super.Draw()

		If (label <> Null) Then
			label.scrollFactor = scrollFactor
			label.Draw()
		End If
	End Method

	Method _ResetHelpers:Void()
		Super._ResetHelpers()

		If (label <> Null) Then
			label.SetWidth(width)
		End If
	End Method

	Method SetSounds:Void(soundOver:String = "", soundOverVolume:Float = 1.0, soundOut:String = "", soundOutVolume:Float = 1.0, soundDown:String = "", soundDownVolume:Float = 1.0, soundUp:String = "", soundUpVolume:Float = 1.0)
		If (soundOver.Length() > 0) Then
			Self.soundOver = FlxG.LoadSound(soundOver, soundOverVolume)
		End If

		If (soundOut.Length() > 0) Then
			Self.soundOut = FlxG.LoadSound(soundOut, soundOutVolume)
		End If

		If (soundDown.Length() > 0) Then
			Self.soundDown = FlxG.LoadSound(soundDown, soundDownVolume)
		End If

		If (soundUp.Length() > 0) Then
			Self.soundUp = FlxG.LoadSound(soundUp, soundUpVolume)
		End If
	End Method

	Method On:Bool() Property
		Return _onToggle
	End Method

	Method On:Void(on:Bool) Property
		_onToggle = on
	End Method

Private
	Method _UpdateButton:Void()
		If (FlxG.Mobile Or FlxG.Mouse.Visible Or FlxG._Game.useSystemCursor) Then
			Local cameras:Stack<FlxCamera> = FlxG.Cameras
			Local camera:FlxCamera
			Local i:Int = 0
			Local l:Int = cameras.Length()
			Local offAll:Bool = True
			Local click:Bool = False

			While (i < l)
				camera = cameras.Get(i)
				FlxG.Mouse.GetWorldPosition(camera, _point)

				If (OverlapsPoint(_point, True, camera)) Then
					offAll = False

					If (FlxG.Mouse.JustPressed()) Then
						status = PRESSED

						If (onDown <> Null) Then
							onDown.OnButtonDown()
						End If

						If (soundDown <> Null) Then
							soundDown.Play(True)
						End If
					End If

					If (FlxG.Mouse.JustReleased() And status = PRESSED) Then
						status = NORMAL
						click = True
					End If

					If (status = NORMAL) Then
						status = HIGHLIGHT

						If (onOver <> Null) Then
							onOver.OnButtonOver()
						End If

						If (soundOver <> Null) Then
							soundOver.Play(True)
						End If
					End If
				End If

				i += 1
			Wend

			If(offAll) Then
				If (status <> NORMAL) Then
					If (onOut <> Null) Then
						onOut.OnButtonOut()
					End If

					If (soundOut <> Null) Then
						soundOut.Play(True)
					End If
				End If

				status = NORMAL
			End If

			If(click) Then
				If (onUp <> Null) Then
					onUp.OnButtonClick()
				End If

				If (soundUp <> Null) Then
					soundUp.Play(True)
				End If
			End If
		End If

		If (label <> Null) Then
			label.x = x
			label.y = y
		End If

		If (labelOffset <> Null) Then
			label.x += labelOffset.x
			label.y += labelOffset.y
		End If

		If (status = HIGHLIGHT And _onToggle) Then
			Frame = NORMAL
		Else
			Frame = status
		End If
	End Method

End Class

Interface FlxButtonClickListener

	Method OnButtonClick:Void()

End Interface

Interface FlxButtonDownListener

	Method OnButtonDown:Void()

End Interface

Interface FlxButtonOverListener

	Method OnButtonOver:Void()

End Interface

Interface FlxButtonOutListener

	Method OnButtonOut:Void()

End Interface

Strict

Import reflection

Import flxextern
Import flxsprite
Import flxtext
Import flxpoint
Import flxsound
Import flxg
Import flxcamera
Import system.flxassetsmanager

Import "data/button_flx.png"
Import "data/beep_flx.mp3"

Class FlxButton Extends FlxSprite

	Global ClassObject:Object

	Const NORMAL:Int = 0

	Const HIGHLIGHT:Int = 1

	Const PRESSED:Int = 2

	Field label:FlxText

	Field labelOffset:FlxPoint

	Field onUp:FlxButtonClickListener

	Field onDown:FlxButtonDownListener

	Field onOver:FlxButtonOverListener

	Field onOut:FlxButtonOutListener

	Field status:Int

	Field soundOver:FlxSound

	Field soundOut:FlxSound

	Field soundDown:FlxSound

	Field soundUp:FlxSound

Private
	Field _onToggle:Bool

	Field _pressed:Bool

	Field _initialized:Bool

Public
	Method New(x:Float = 0, y:Float = 0, label:String = "", onClick:FlxButtonClickListener = Null, driver:ClassInfo = Null)
		Super.New(x, y)

		If (label.Length() > 0) Then
			Self.label = New FlxText(0, 0, 80, label, driver)
			Self.label.SetFormat(FlxText.SYSTEM_FONT, 8, $FF333333, FlxText.ALIGN_CENTER)
			labelOffset = New FlxPoint(-1, 3)
		End If

		LoadGraphic("button" + FlxG.DATA_SUFFIX, True, False, 80, 20)
		onUp = onClick
		onDown = Null
		onOut = Null
		onOver = Null

		soundOver = Null
		soundOut = Null
		soundDown = Null
		soundUp = Null

		status = NORMAL
		_onToggle = False
		_pressed = False
	End Method

	Method Destroy:Void()
		If (label <> Null) Then
			label.Destroy()
			label = Null
		End If

		onUp = Null
		onDown = Null
		onOut = Null
		onOver = Null

		If (soundOver <> Null) Then
			soundOver.Destroy()
			soundOver = Null
		End If

		If (soundOut <> Null) Then
			soundOut.Destroy()
			soundOut = Null
		End If

		If (soundDown <> Null) Then
			soundDown.Destroy()
			soundDown = Null
		End If

		If (soundUp <> Null) Then
			soundUp.Destroy()
			soundUp = Null
		End If

		Super.Destroy()
	End Method

	Method Update:Void()
		_UpdateButton()

		If (label = Null) Return

		Select (Frame)
			Case HIGHLIGHT
				label.Alpha = 1.0

			Case PRESSED
				label.Alpha = .5
				label.y += 1

			Case NORMAL
				label.Alpha = .8

			Default
				label.Alpha = .8
		End Select
	End Method

	Method Draw:Void()
		Super.Draw()

		If (label <> Null) Then
			label.scrollFactor = scrollFactor
			label.Draw()
		End If
	End Method

	Method _ResetHelpers:Void()
		Super._ResetHelpers()

		If (label <> Null) Then
			label.SetWidth(width)
		End If
	End Method

	Method SetSounds:Void(soundOver:String = "", soundOverVolume:Float = 1.0, soundOut:String = "", soundOutVolume:Float = 1.0, soundDown:String = "", soundDownVolume:Float = 1.0, soundUp:String = "", soundUpVolume:Float = 1.0)
		If (soundOver.Length() > 0) Then
			Self.soundOver = FlxG.LoadSound(soundOver, soundOverVolume)
		End If

		If (soundOut.Length() > 0) Then
			Self.soundOut = FlxG.LoadSound(soundOut, soundOutVolume)
		End If

		If (soundDown.Length() > 0) Then
			Self.soundDown = FlxG.LoadSound(soundDown, soundDownVolume)
		End If

		If (soundUp.Length() > 0) Then
			Self.soundUp = FlxG.LoadSound(soundUp, soundUpVolume)
		End If
	End Method

	Method On:Bool() Property
		Return _onToggle
	End Method

	Method On:Void(on:Bool) Property
		_onToggle = on
	End Method

Private
	Method _UpdateButton:Void()
		If (FlxG.Mobile Or FlxG.Mouse.Visible Or FlxG._Game.useSystemCursor) Then
			Local cameras:Stack<FlxCamera> = FlxG.Cameras
			Local camera:FlxCamera
			Local i:Int = 0
			Local l:Int = cameras.Length()
			Local offAll:Bool = True
			Local click:Bool = False

			While (i < l)
				camera = cameras.Get(i)
				FlxG.Mouse.GetWorldPosition(camera, _point)

				If (OverlapsPoint(_point, True, camera)) Then
					offAll = False

					If (FlxG.Mouse.JustPressed()) Then
						status = PRESSED

						If (onDown <> Null) Then
							onDown.OnButtonDown()
						End If

						If (soundDown <> Null) Then
							soundDown.Play(True)
						End If
					End If

					If (FlxG.Mouse.JustReleased() And status = PRESSED) Then
						status = NORMAL
						click = True
					End If

					If (status = NORMAL) Then
						status = HIGHLIGHT

						If (onOver <> Null) Then
							onOver.OnButtonOver()
						End If

						If (soundOver <> Null) Then
							soundOver.Play(True)
						End If
					End If
				End If

				i += 1
			Wend

			If(offAll) Then
				If (status <> NORMAL) Then
					If (onOut <> Null) Then
						onOut.OnButtonOut()
					End If

					If (soundOut <> Null) Then
						soundOut.Play(True)
					End If
				End If

				status = NORMAL
			End If

			If(click) Then
				If (onUp <> Null) Then
					onUp.OnButtonClick()
				End If

				If (soundUp <> Null) Then
					soundUp.Play(True)
				End If
			End If
		End If

		If (label <> Null) Then
			label.x = x
			label.y = y
		End If

		If (labelOffset <> Null) Then
			label.x += labelOffset.x
			label.y += labelOffset.y
		End If

		If (status = HIGHLIGHT And _onToggle) Then
			Frame = NORMAL
		Else
			Frame = status
		End If
	End Method

End Class

Interface FlxButtonClickListener

	Method OnButtonClick:Void()

End Interface

Interface FlxButtonDownListener

	Method OnButtonDown:Void()

End Interface

Interface FlxButtonOverListener

	Method OnButtonOver:Void()

End Interface

Interface FlxButtonOutListener

	Method OnButtonOut:Void()

End Interface


Fixed a typo

Fixed a typo

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

#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}: "
Const LogTemplate:String = "[Info] {Debug}: "

' 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)
	#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)
	' Allocate a new data-buffer.
	Local B:= New DataBuffer(Size)

	' Copy the buffer's bytes over to 'B'.
	Buffer.CopyBytes(0, B, 0, Min(Size, Buffer.Length()))

	' 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

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 imagedemensions
Import retrostrings
Import stringutil

#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}: "
Const LogTemplate:String = "[Info] {Debug}: "

' 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)
	#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)
	' Allocate a new data-buffer.
	Local B:= New DataBuffer(Size)

	' Copy the buffer's bytes over to 'B'.
	Buffer.CopyBytes(0, B, 0, Min(Size, Buffer.Length()))

	' 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