Smart FPS using added

Smart FPS using added

Strict

Import mojo

Import flxextern
Import flxbasic
Import flxstate
Import flxcamera
Import flxtimer
Import flxtext
Import flxg
Import flxu
Import flxtilemap
Import system.flxassetsmanager
Import system.flxfont
Import system.flxdebugger
Import system.flxreplay
Import plugin.timermanager

Import "data/flx_beep.mp3"

'#HTML5_SUSPEND_ON_BLUR_ENABLED = "true"

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field useVirtualResolution:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Int

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:FlxClass

	Field _created:Bool

	Field _lostFocus:Bool

	Field _step:Int

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _updatesCounter:FlxFPSCounter

	Field _rendersCounter:FlxFPSCounter

Public
	Method New(gameSizeX:Int, gameSizeY:Int, initialState:FlxClass, zoom:Float = 1, framerate:Int = 60, useSystemCursor:Bool = False)
		_lostFocus = False

		FlxG.Init(Self, gameSizeX, gameSizeY, zoom)
		FlxG.Framerate = framerate

		useSoundHotKeys = Not IsMobile()
		Self.useSystemCursor = useSystemCursor
		useVirtualResolution = True
		If (Not useSystemCursor) HideMouse()
		_debuggerUp = False

		_state = Null

		_replay = New FlxReplay()
		_replayRequested = False
		_recordingRequested = False
		_replaying = False
		_recording = False

		_iState = initialState
		_requestedState = Null
		_requestedReset = True
		_created = False

		_soundTrayTimer = 0
		_soundTrayWidth = 160
		_soundTrayHeight = 60
		_soundTrayX = 0
		_soundTrayY	= -_soundTrayHeight
		_soundTrayVisible = False

		_updatesCounter = New FlxFPSCounter()
		_rendersCounter = New FlxFPSCounter()
	End Method

	Method OnCreate:Int()
		_Reset()
		_InitData()
		_Step()

		_soundTrayX	= (FlxG.Width / 2) * FlxCamera.DefaultZoom - (_soundTrayWidth / 2)
		_soundTrayLabel = New FlxText(10, 32, _soundTrayWidth, "VOLUME")
		_soundTrayLabel.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_CENTER)
		Return 0
	End Method

	Method OnUpdate:Int()
		If (_rendersCounter.FPS >= 0 And _rendersCounter.FPS < 30) Then
			_updatesCounter.Update()

			FlxG.Elapsed = FlxG.TimeScale * (1.0 / _updatesCounter.FPS)
			_Step()
		End If
		Return 0
	End Method

	Method OnRender:Int()
		If (Not _replaying) Then
			_rendersCounter.Update()
		End If

		If (_rendersCounter.FPS >= 30) Then
			FlxG.Elapsed = FlxG.TimeScale * (1.0 / _rendersCounter.FPS)
			_Step()
		End If

		Cls(FlxG._BgColor.r, FlxG._BgColor.g, FlxG._BgColor.b)
		Scale(FlxG._DeviceScaleFactorX, FlxG._DeviceScaleFactorY)

		FlxG._LastDrawingColor = FlxG.WHITE
		FlxG._LastDrawingBlend = GetBlend()
		FlxG._LastDrawingAlpha = GetAlpha()
		FlxG._CurrentCamera = Null

		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG._CurrentCamera = FlxG.Cameras.Get(i)

			If (Not FlxG._CurrentCamera.active) Then
				i+=1
				Continue
			End If

			FlxG._CurrentCameraID = i

			If (FlxG._CurrentCamera = Null Or Not FlxG._CurrentCamera.exists Or Not FlxG._CurrentCamera.visible) Continue

			FlxG._CurrentCamera.DrawFX() 'not realy draw. Only calculation
			FlxG._CurrentCamera.Lock()
			_state.Draw()
			FlxG.DrawPlugins()
			FlxG._CurrentCamera.Unlock()

			i+=1
		Wend

		#If TARGET <> "ios" Or TARGET <> "android"
			If (_soundTrayVisible) Then
				Local globalVolume:Int = FlxU.Round(FlxG.Volume() * 10)
				If (FlxG.Mute) globalVolume = 0

				Local bx:Int = 20
				Local by:Int = 28

				If (FlxG._LastDrawingColor <> FlxG.WHITE) Then
					SetColor(255, 255, 255)
					FlxG._LastDrawingColor = FlxG.WHITE
				End If

				PushMatrix()
				Translate(_soundTrayX, _soundTrayY)

				Local i:Int = 0
				While (i < 10)
					If (i < globalVolume) Then
						If (FlxG._LastDrawingAlpha <> 1) Then
							SetAlpha(1)
							FlxG._LastDrawingAlpha = 1
						End If

					Else
						If (FlxG._LastDrawingAlpha <> .5) Then
							SetAlpha(.5)
							FlxG._LastDrawingAlpha = .5
						End If
					End If

					DrawRect(bx, by, 8, i * 2)

					bx += 12
					by -= 2
					i += 1
				Wend

				_soundTrayLabel.Draw()

				PopMatrix()
			End If
		#End

		FlxG.Mouse.Draw()

		#If CONFIG = "debug"
			SetColor(255, 255, 255)
			DrawText("FPS: " + _renderFPS, 10, 10)
		#End

		Return 0
	End Method

	Method OnSuspend:Int()
		ShowMouse()
		FlxG.PauseSounds()
		Return 0
	End Method

	Method OnResume:Int()
		If (Not _debuggerUp And useSystemCursor) Then
			HideMouse()
		End If
		FlxG.ResetInput()
		FlxG.ResumeSounds()
		Return 0
	End Method

	Method OnContentInit:Void()
	End Method

Private
	Method _ShowSoundTray:Void(silent:Bool = False)
		If (Not silent) Then
			FlxG.Play(FlxG.DATA_PREFIX + "beep")
		End If

		_soundTrayTimer = 1
		_soundTrayY = 0
		_soundTrayVisible = True
	End Method

	Method _SwitchState:Void()
		FlxG.ResetCameras()

		Local timeManager:TimerManager = FlxTimer.Manager()
		If (timeManager <> Null) timeManager.Clear()

		If (_state <> Null) _state.Destroy()

		_state = _requestedState
		_state.Create()
	End Method

	Method _Step:Void()
		#If TARGET <> "ios" Or TARGET <> "android"
			If (useSoundHotKeys) Then
				If (KeyHit(KEY_0)) Then
					FlxG.Mute = Not FlxG.Mute

					If (FlxG.VolumeHandler <> Null) Then
						If (FlxG.Mute) Then
							FlxG.VolumeHandler.OnVolumeChange(0)
						Else
							FlxG.VolumeHandler.OnVolumeChange(FlxG.Volume())
						End If
					End If

					_ShowSoundTray()
				End If

				If (KeyHit(KEY_MINUS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() - .1)
					_ShowSoundTray()
				End If

				If (KeyHit(KEY_EQUALS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() + .1)
					_ShowSoundTray()
				End If
			End If

			_UpdateSoundTray()
		#End

		If (_requestedReset) Then
			_requestedReset = False
			_requestedState = FlxState(_iState.CreateInstance())
			_replayTimer = 0
			_replayCancelKeys = []
			_Reset()
			FlxG.Reset()
		End If

		If (_recordingRequested) Then
			_recordingRequested = False
			_replay.Create(FlxG.GlobalSeed)
			_recording = True

			If (_debugger <> Null) Then
				'TODO
				FlxG.Log("FLIXEL: starting new flixel gameplay record.")
			End If

		ElseIf (_replayRequested)
			_replayRequested = False
			_replay.Rewind()
			FlxG.GlobalSeed = _replay.seed

			If (_debugger <> Null) Then
				'TODO
			End If

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ActiveCount = 0

		If (_replaying) Then
			If (_replayCancelKeys.Length() > 0 And Not KeyDown(192) And Not KeyDown(220)) Then
				Local i:Int = 0
				Local l:Int = _replayCancelKeys.Length()

				While (i < l)
					If (KeyDown(_replayCancelKeys[i])) Then
						If (_replayCallback <> Null) Then
							_replayCallback.OnReplayComplete()
							_replayCallback = Null
						Else
							FlxG.StopReplay()
						End If

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

				If (_replayTimer <= 0) Then
					If (_replayCallback <> Null) Then
						_replayCallback.OnReplayComplete()
						_replayCallback = Null
					Else
						FlxG.StopReplay()
					End If
				End If
			End If

			If (_replaying And _replay.finished) Then
				FlxG.StopReplay()

				If (_replayCallback <> Null) Then
					_replayCallback.OnReplayComplete()
					_replayCallback = Null
				End If
			End If

			If (_debugger <> Null) Then
				'TODO
			End If
		Else
			FlxG.UpdateInput()
		End If

		If (_recording) Then
			_replay.RecordFrame()

			If (_debugger <> Null) Then
				'TODO
			End If
		End If

		_Update()

		If (_debugger <> Null) Then
			'TODO
		End If
	End Method

	Method _UpdateSoundTray:Void()
		If (Not _soundTrayVisible) Return

		If (_soundTrayTimer > 0) Then
			_soundTrayTimer -= FlxG.Elapsed

		ElseIf (_soundTrayY > -_soundTrayHeight)
			_soundTrayY =_soundTrayY - (FlxG.Elapsed * FlxG.Height * 2)

			If (_soundTrayY <= -_soundTrayHeight) Then
				_soundTrayVisible = False

				'TODO Save sound settings
			End If
		End If
	End Method

	Method _Update:Void()
		FlxG.UpdateSounds()
		FlxG.UpdatePlugins()
		_state.Update()
		FlxG.UpdateCameras()
	End Method

	Method _Reset:Void()
		SetUpdateRate(FlxG.Framerate)
		Seed = SystemMillisecs()

		FlxG.DeviceWidth = DeviceWidth()
		FlxG.DeviceHeight = DeviceHeight()

		If (useVirtualResolution) Then
			FlxG._DeviceScaleFactorX = FlxG.DeviceWidth / Float(FlxG.Width)
			FlxG._DeviceScaleFactorY = FlxG.DeviceHeight / Float(FlxG.Height)
		Else
			FlxG._DeviceScaleFactorX = 1
			FlxG._DeviceScaleFactorY = 1
		End If

		_step = 1000.0 / FlxG.Framerate

		_updatesCounter.Reset()
		_rendersCounter.Reset()
	End Method

	Method _InitData:Void()
		FlxAssetsManager.Init()

		Local minSystemFontSize:Int = 8
		Local maxSystemFontSize:Int = 24
		Local fontPathPrefix:String = FlxG.DATA_PREFIX + FlxText.SYSTEM_FONT + "_font_"

		Local system:FlxFont = 	FlxAssetsManager.AddFont(FlxText.SYSTEM_FONT)

		For Local size:Int = minSystemFontSize To maxSystemFontSize
			system.SetPath(size, fontPathPrefix + Min(size, 17) + ".png")
		Next

		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "default", FlxG.DATA_PREFIX + "default.png")
		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "button", FlxG.DATA_PREFIX + "button.png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES, FlxTilemap.AUTOTILES + ".png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES_ALT, FlxTilemap.AUTOTILES_ALT + ".png")
		FlxAssetsManager.AddCursor(FlxG.DATA_PREFIX + "cursor", FlxG.DATA_PREFIX + "cursor.png")
		FlxAssetsManager.AddSound(FlxG.DATA_PREFIX + "beep", FlxG.DATA_PREFIX + "beep.mp3")

		Self.OnContentInit()
	End Method
End Class

Private
Class FlxFPSCounter

	Field FPS:Int

Private
	Field _FPSCounter:Int

	Field _lastFPSCounter:Int

	Field _lastUpdateTime:Int

Public
	Method New()
		Reset()
	End Method

	Method Update:Void()
		If (FPS < 0) Then
			_lastUpdateTime = Millisecs()
			FPS = FlxG.Framerate
			_lastFPSCounter = FPS
		Else
			If (Millisecs() - _lastUpdateTime > 1000) Then
				If (Abs(_lastFPSCounter - _FPSCounter) < 10) Then
					FPS = _FPSCounter
				End If

				_lastFPSCounter = _FPSCounter
				_FPSCounter = 0
				_lastUpdateTime = Millisecs()
			Else
				_FPSCounter += 1
			End If
		End If
	End Method

	Method Reset:Void()
		FPS = -1
		_FPSCounter = 0
		_lastFPSCounter = 0
		_lastUpdateTime = 0
	End Method

End Class

Strict

Import mojo

Import flxextern
Import flxbasic
Import flxstate
Import flxcamera
Import flxtimer
Import flxtext
Import flxg
Import flxu
Import flxtilemap
Import system.flxassetsmanager
Import system.flxfont
Import system.flxdebugger
Import system.flxreplay
Import plugin.timermanager

Import "data/flx_beep.mp3"

'#HTML5_SUSPEND_ON_BLUR_ENABLED = "true"

Class FlxGame extends App

	Field useSoundHotKeys:Bool

	Field useSystemCursor:Bool

	Field useVirtualResolution:Bool

	Field forceDebugger:Bool

	Field _state:FlxState

	Field _requestedState:FlxState

	Field _requestedReset:Bool

	Field _debugger:FlxDebugger

	Field _debuggerUp:Bool

	Field _replay:FlxReplay

	Field _replayRequested:Bool

	Field _recordingRequested:Bool

	Field _replaying:Bool

	Field _recording:Bool

	Field _replayCancelKeys:Int[]

	Field _replayTimer:Int

	Field _replayCallback:FlxReplayListener

Private
	Field _iState:FlxClass

	Field _created:Bool

	Field _lostFocus:Bool

	Field _step:Int

	Field _soundTrayTimer:Float

	Field _soundTrayWidth:Float

	Field _soundTrayHeight:Float

	Field _soundTrayX:Float

	Field _soundTrayY:Float

	Field _soundTrayVisible:Bool

	Field _soundTrayLabel:FlxText

	Field _fps:Int

	Field _fpsCounter:Int

	Field _lastFpsCounter:Int

	Field _fpsTime:Int

Public
	Method New(gameSizeX:Int, gameSizeY:Int, initialState:FlxClass, zoom:Float = 1, framerate:Int = 60, useSystemCursor:Bool = False)
		_lostFocus = False

		FlxG.Init(Self, gameSizeX, gameSizeY, zoom)
		FlxG.Framerate = framerate

		useSoundHotKeys = Not IsMobile()
		Self.useSystemCursor = useSystemCursor
		useVirtualResolution = True
		If (Not useSystemCursor) HideMouse()
		_debuggerUp = False

		_state = Null

		_replay = New FlxReplay()
		_replayRequested = False
		_recordingRequested = False
		_replaying = False
		_recording = False

		_iState = initialState
		_requestedState = Null
		_requestedReset = True
		_created = False

		_soundTrayTimer = 0
		_soundTrayWidth = 160
		_soundTrayHeight = 60
		_soundTrayX = 0
		_soundTrayY	= -_soundTrayHeight
		_soundTrayVisible = False
	End Method

	Method OnCreate:Int()
		_Reset()
		_InitData()
		_Step()

		_soundTrayX	= (FlxG.Width / 2) * FlxCamera.DefaultZoom - (_soundTrayWidth / 2)
		_soundTrayLabel = New FlxText(10, 32, _soundTrayWidth, "VOLUME")
		_soundTrayLabel.SetFormat(FlxText.SYSTEM_FONT, 16, FlxG.WHITE, FlxText.ALIGN_CENTER)
		Return 0
	End Method

	Method OnRender:Int()
		If (Not _replaying) Then
			If (_fps < 0) Then
				_fpsTime = Millisecs()
				_fps = FlxG.Framerate
				_lastFpsCounter = _fps
			Else
				If (Millisecs() - _fpsTime > 1000) Then
					If (Abs(_lastFpsCounter - _fpsCounter) < 10) Then
						_fps = _fpsCounter
					End If

					_lastFpsCounter = _fpsCounter
					_fpsCounter = 0
					_fpsTime = Millisecs()
				Else
					_fpsCounter += 1
				End If
			End If
		End If

		'Real elapsed very unstable in Monkey. TODO!
		FlxG.Elapsed = FlxG.TimeScale * (1.0 / _fps)

		#If TARGET <> "ios" Or TARGET <> "android"
			If (useSoundHotKeys) Then
				If (KeyHit(KEY_0)) Then
					FlxG.Mute = Not FlxG.Mute

					If (FlxG.VolumeHandler <> Null) Then
						If (FlxG.Mute) Then
							FlxG.VolumeHandler.OnVolumeChange(0)
						Else
							FlxG.VolumeHandler.OnVolumeChange(FlxG.Volume())
						End If
					End If

					_ShowSoundTray()
				End If

				If (KeyHit(KEY_MINUS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() - .1)
					_ShowSoundTray()
				End If

				If (KeyHit(KEY_EQUALS)) Then
					FlxG.Mute = False
					FlxG.Volume(FlxG.Volume() + .1)
					_ShowSoundTray()
				End If
			End If

			_UpdateSoundTray()
		#End

		_Step()

		Cls(FlxG._BgColor.r, FlxG._BgColor.g, FlxG._BgColor.b)
		Scale(FlxG._DeviceScaleFactorX, FlxG._DeviceScaleFactorY)

		FlxG._LastDrawingColor = FlxG.WHITE
		FlxG._LastDrawingBlend = GetBlend()
		FlxG._LastDrawingAlpha = GetAlpha()
		FlxG._CurrentCamera = Null

		Local i:Int = 0
		Local l:Int = FlxG.Cameras.Length()

		While(i < l)
			FlxG._CurrentCamera = FlxG.Cameras.Get(i)

			If (Not FlxG._CurrentCamera.active) Then
				i+=1
				Continue
			End If

			FlxG._CurrentCameraID = i

			If (FlxG._CurrentCamera = Null Or Not FlxG._CurrentCamera.exists Or Not FlxG._CurrentCamera.visible) Continue

			FlxG._CurrentCamera.DrawFX() 'not realy draw. Only calculation
			FlxG._CurrentCamera.Lock()
			_state.Draw()
			FlxG.DrawPlugins()
			FlxG._CurrentCamera.Unlock()

			i+=1
		Wend

		#If TARGET <> "ios" Or TARGET <> "android"
			If (_soundTrayVisible) Then
				Local globalVolume:Int = FlxU.Round(FlxG.Volume() * 10)
				If (FlxG.Mute) globalVolume = 0

				Local bx:Int = 20
				Local by:Int = 28

				If (FlxG._LastDrawingColor <> FlxG.WHITE) Then
					SetColor(255, 255, 255)
					FlxG._LastDrawingColor = FlxG.WHITE
				End If

				PushMatrix()
				Translate(_soundTrayX, _soundTrayY)

				Local i:Int = 0
				While (i < 10)
					If (i < globalVolume) Then
						If (FlxG._LastDrawingAlpha <> 1) Then
							SetAlpha(1)
							FlxG._LastDrawingAlpha = 1
						End If

					Else
						If (FlxG._LastDrawingAlpha <> .5) Then
							SetAlpha(.5)
							FlxG._LastDrawingAlpha = .5
						End If
					End If

					DrawRect(bx, by, 8, i * 2)

					bx += 12
					by -= 2
					i += 1
				Wend

				_soundTrayLabel.Draw()

				PopMatrix()
			End If
		#End

		FlxG.Mouse.Draw()

		#If CONFIG = "debug"
			SetColor(255, 255, 255)
			DrawText("FPS: " + _fps, 10, 10)
		#End

		Return 0
	End Method

	Method OnSuspend:Int()
		ShowMouse()
		FlxG.PauseSounds()
		Return 0
	End Method

	Method OnResume:Int()
		If (Not _debuggerUp And useSystemCursor) Then
			HideMouse()
		End If
		FlxG.ResetInput()
		FlxG.ResumeSounds()
		Return 0
	End Method

	Method OnContentInit:Void()
	End Method

Private
	Method _ShowSoundTray:Void(silent:Bool = False)
		If (Not silent) Then
			FlxG.Play(FlxG.DATA_PREFIX + "beep")
		End If

		_soundTrayTimer = 1
		_soundTrayY = 0
		_soundTrayVisible = True
	End Method

	Method _SwitchState:Void()
		FlxG.ResetCameras()

		Local timeManager:TimerManager = FlxTimer.Manager()
		If (timeManager <> Null) timeManager.Clear()

		If (_state <> Null) _state.Destroy()

		_state = _requestedState
		_state.Create()
	End Method

	Method _Step:Void()
		If (_requestedReset) Then
			_requestedReset = False
			_requestedState = FlxState(_iState.CreateInstance())
			_replayTimer = 0
			_replayCancelKeys = []
			_Reset()
			FlxG.Reset()
		End If

		If (_recordingRequested) Then
			_recordingRequested = False
			_replay.Create(FlxG.GlobalSeed)
			_recording = True

			If (_debugger <> Null) Then
				'TODO
				FlxG.Log("FLIXEL: starting new flixel gameplay record.")
			End If

		ElseIf (_replayRequested)
			_replayRequested = False
			_replay.Rewind()
			FlxG.GlobalSeed = _replay.seed

			If (_debugger <> Null) Then
				'TODO
			End If

			_replaying = True
		End If

		If (_state <> _requestedState) _SwitchState()

		FlxBasic._ActiveCount = 0

		If (_replaying) Then
			If (_replayCancelKeys.Length() > 0 And Not KeyDown(192) And Not KeyDown(220)) Then
				Local i:Int = 0
				Local l:Int = _replayCancelKeys.Length()

				While (i < l)
					If (KeyDown(_replayCancelKeys[i])) Then
						If (_replayCallback <> Null) Then
							_replayCallback.OnReplayComplete()
							_replayCallback = Null
						Else
							FlxG.StopReplay()
						End If

						Exit
					End If

					i += 1
				Wend
			End If

			_replay.PlayNextFrame()

			If (_replayTimer > 0) Then
				_replayTimer -= _step

				If (_replayTimer <= 0) Then
					If (_replayCallback <> Null) Then
						_replayCallback.OnReplayComplete()
						_replayCallback = Null
					Else
						FlxG.StopReplay()
					End If
				End If
			End If

			If (_replaying And _replay.finished) Then
				FlxG.StopReplay()

				If (_replayCallback <> Null) Then
					_replayCallback.OnReplayComplete()
					_replayCallback = Null
				End If
			End If

			If (_debugger <> Null) Then
				'TODO
			End If
		Else
			FlxG.UpdateInput()
		End If

		If (_recording) Then
			_replay.RecordFrame()

			If (_debugger <> Null) Then
				'TODO
			End If
		End If

		_Update()

		If (_debugger <> Null) Then
			'TODO
		End If
	End Method

	Method _UpdateSoundTray:Void()
		If (Not _soundTrayVisible) Return

		If (_soundTrayTimer > 0) Then
			_soundTrayTimer -= FlxG.Elapsed

		ElseIf (_soundTrayY > -_soundTrayHeight)
			_soundTrayY =_soundTrayY - (FlxG.Elapsed * FlxG.Height * 2)

			If (_soundTrayY <= -_soundTrayHeight) Then
				_soundTrayVisible = False

				'TODO Save sound settings
			End If
		End If
	End Method

	Method _Update:Void()
		FlxG.UpdateSounds()
		FlxG.UpdatePlugins()
		_state.Update()
		FlxG.UpdateCameras()
	End Method

	Method _Reset:Void()
		SetUpdateRate(FlxG.Framerate)
		Seed = SystemMillisecs()

		FlxG.DeviceWidth = DeviceWidth()
		FlxG.DeviceHeight = DeviceHeight()

		If (useVirtualResolution) Then
			FlxG._DeviceScaleFactorX = FlxG.DeviceWidth / Float(FlxG.Width)
			FlxG._DeviceScaleFactorY = FlxG.DeviceHeight / Float(FlxG.Height)
		Else
			FlxG._DeviceScaleFactorX = 1
			FlxG._DeviceScaleFactorY = 1
		End If

		_step = 1000.0 / FlxG.Framerate
		_fps = -1
	End Method

	Method _InitData:Void()
		FlxAssetsManager.Init()

		Local minSystemFontSize:Int = 8
		Local maxSystemFontSize:Int = 24
		Local fontPathPrefix:String = FlxG.DATA_PREFIX + FlxText.SYSTEM_FONT + "_font_"

		Local system:FlxFont = 	FlxAssetsManager.AddFont(FlxText.SYSTEM_FONT)

		For Local size:Int = minSystemFontSize To maxSystemFontSize
			system.SetPath(size, fontPathPrefix + Min(size, 17) + ".png")
		Next

		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "default", FlxG.DATA_PREFIX + "default.png")
		FlxAssetsManager.AddImage(FlxG.DATA_PREFIX + "button", FlxG.DATA_PREFIX + "button.png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES, FlxTilemap.AUTOTILES + ".png")
		FlxAssetsManager.AddImage(FlxTilemap.AUTOTILES_ALT, FlxTilemap.AUTOTILES_ALT + ".png")
		FlxAssetsManager.AddCursor(FlxG.DATA_PREFIX + "cursor", FlxG.DATA_PREFIX + "cursor.png")
		FlxAssetsManager.AddSound(FlxG.DATA_PREFIX + "beep", FlxG.DATA_PREFIX + "beep.mp3")

		Self.OnContentInit()
	End Method
End Class


add param name to debug log

add param name to debug log

Strict

Private

Import bono
Import bono.src.payment.paymentprovider
Import bono.src.payment.paymentprovideralias

Public

Class PaymentManager Implements PaymentProvider
    Private

    Field provider:PaymentProvider
    Global instance:PaymentManager

    Public

    Field idPrefix:String = ""
    Field idAlias:StringMap<String> = New StringMap<String>()

    Method New()
        Throw New InvalidConstructorException("use New(PaymentProvider)")
    End

    Method New(provider:PaymentProvider)
        Self.provider = provider
    End

    Method IsPurchased:Bool(id:String)
        Return GetProvider().IsPurchased(ResolveId(id))
    End

    Method Purchase:Void(id:String)
        DebugLog("Purchase", "id: " + ResolveId(id))
        GetProvider().Purchase(ResolveId(id))
    End

    Method IsProcessing:Bool()
        Return GetProvider().IsProcessing()
    End

    Method RestorePreviousPurchases:Void()
        DebugLog("RestorePreviousPurchases")
        #If TARGET="ios"
        PaymentProviderAlias(GetProvider()).RestorePreviousPurchases()
        #End
    End

    Method SetPublicKey:Void(key:String)
        DebugLog("SetPublicKey")
        #If TARGET="android" And BONO_ANDROID_MARKET="google"
        PaymentProviderAlias(GetProvider()).publicKey = key
        #End
    End

    Method SetItemGroupId:Void(itemGroupId:String)
        DebugLog("SetItemGroupId")
        #If TARGET="android" And BONO_ANDROID_MARKET="samsung"
        PaymentProviderAlias(GetProvider()).itemGroupId = itemGroupId
        #End
    End

    Method GetProvider:PaymentProvider()
        Return provider
    End

    Function GetInstance:PaymentManager()
        If Not instance
            instance = New PaymentManager(New PaymentProviderAlias())
        End

        Return instance
    End

    Private

    Method ResolveId:String(id:String)
        If idAlias.Contains(id)
            Return idPrefix + idAlias.Get(id)
        Else
            Return idPrefix + id
        End
    End

    Method DebugLog:Void(func:String, message:String="")
        #If CONFIG="debug"
        Print "[PaymentManager " + func + "] " + message
        #End
    End
End

Strict

Private

Import bono
Import bono.src.payment.paymentprovider
Import bono.src.payment.paymentprovideralias

Public

Class PaymentManager Implements PaymentProvider
    Private

    Field provider:PaymentProvider
    Global instance:PaymentManager

    Public

    Field idPrefix:String = ""
    Field idAlias:StringMap<String> = New StringMap<String>()

    Method New()
        Throw New InvalidConstructorException("use New(PaymentProvider)")
    End

    Method New(provider:PaymentProvider)
        Self.provider = provider
    End

    Method IsPurchased:Bool(id:String)
        Return GetProvider().IsPurchased(ResolveId(id))
    End

    Method Purchase:Void(id:String)
        DebugLog("Purchase", ResolveId(id))
        GetProvider().Purchase(ResolveId(id))
    End

    Method IsProcessing:Bool()
        Return GetProvider().IsProcessing()
    End

    Method RestorePreviousPurchases:Void()
        DebugLog("RestorePreviousPurchases")
        #If TARGET="ios"
        PaymentProviderAlias(GetProvider()).RestorePreviousPurchases()
        #End
    End

    Method SetPublicKey:Void(key:String)
        DebugLog("SetPublicKey")
        #If TARGET="android" And BONO_ANDROID_MARKET="google"
        PaymentProviderAlias(GetProvider()).publicKey = key
        #End
    End

    Method SetItemGroupId:Void(itemGroupId:String)
        DebugLog("SetItemGroupId")
        #If TARGET="android" And BONO_ANDROID_MARKET="samsung"
        PaymentProviderAlias(GetProvider()).itemGroupId = itemGroupId
        #End
    End

    Method GetProvider:PaymentProvider()
        Return provider
    End

    Function GetInstance:PaymentManager()
        If Not instance
            instance = New PaymentManager(New PaymentProviderAlias())
        End

        Return instance
    End

    Private

    Method ResolveId:String(id:String)
        If idAlias.Contains(id)
            Return idPrefix + idAlias.Get(id)
        Else
            Return idPrefix + id
        End
    End

    Method DebugLog:Void(func:String, message:String="")
        #If CONFIG="debug"
        Print "[PaymentManager " + func + "] " + message
        #End
    End
End


Changed 'PublicDataStream.New' to protected.

Strict

Public

' Preprocessor related:
#IOUTIL_PUBLICDATASTREAM_LEGACY_BIG_ENDIAN = True

' 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 New()
		' Nothing so far.
	End

	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
			Local Value:= Super.ReadShort()

			#If IOUTIL_PUBLICDATASTREAM_LEGACY_BIG_ENDIAN
				Return NToHS(Value)
			#Else
				Return NToHS_S(Value)
			#End
		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:
#IOUTIL_PUBLICDATASTREAM_LEGACY_BIG_ENDIAN = True

' 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
			Local Value:= Super.ReadShort()

			#If IOUTIL_PUBLICDATASTREAM_LEGACY_BIG_ENDIAN
				Return NToHS(Value)
			#Else
				Return NToHS_S(Value)
			#End
		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