' CarWorks (car creating and editing tool for Stunts)
' Copyright (C) 2021-2022 Lucas Pedrosa

' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, version 3 of the License.

' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
' GNU General Public License for more details.

' You should have received a copy of the GNU General Public License
' along with this program.  If not, see <http://www.gnu.org/licenses/>.

' COMPILATION:
' CarWorks has been compiled with FreeBasic 1.07.1 for GNU/Linux,
' and Windows. It should compile well with any newer version
' and likely, with some older versions. You can get FreeBasic at:
' - http://www.freebasic.net
' For GNU/Linux, compile with:
' - fbc carworks.bas
' For Windows, compile with:
' - fbc -s gui carworks.bas
'
' Comment the line #define __USE_SOIL__ for direct compilation
' This will stop CarWorks from incorporating the library
' FBImage/SOIL/stb_image, but will drop support for some picture
' formats except via ImageMagick if it's installed. If you do
' want to use this library, you'll have to compile it first
' into a static binary and put it in a "liblin" or "libwin"
' subdirectory. The source code is provided in SOIL-src.zip.

#include "targalib.bi"
#ifdef __FB_LINUX__
	#define SEP "/"
#else
	#define SEP "\"
#endif

#define __USE_SOIL__	'Use FBimage/SOIL/stb_image library
#ifdef __USE_SOIL__
	#include once "FBImage.bi"
#endif

#define SWIDTH 960
#define SHEIGHT 648
#define PI 3.14159

#define B_COL RGB(50, 20, 30)
#define H_COL RGB(60, 30, 40)
#define SEL_COL RGB(40, 10, 20)
#define DARK_COL RGB(25, 10, 15)
#define BRIGHT_COL RGB(100, 40, 60)
#define TEXT_COL RGB(220, 120, 150)
#define GRAY_COL RGB(180, 180, 180)
#define WHITE_COL RGB(250, 250, 250)
#define NUM_COL RGB(0, 180, 100)
#define PROGRAM_VERSION "1.1"
#define PROGRAM_RELEASE __DATE_ISO__
#define PROGRAM_TITLE "CarWorks " & PROGRAM_VERSION

Declare Sub UpdatePG (reverse As Byte = 0)
Declare Sub ResizeWheels (dw As Double, drx As Double, dri As Double)
Declare Sub GenerateBoundingBox (model As Byte)
Declare Sub LoadVSH (finame As String)
Declare Sub UpdateDashItems
Declare Function Config (id As String) As String
Declare Function FileHandling (ftype As Byte = 1) As String

Dim Shared menuimg As ULong Ptr, curmenu As Byte

Type Vertex
	x As Double
	y As Double
	z As Double
	sel As Byte				'Selected or similar
End Type

Type VisualVertex
	x As Double
	y As Double
	z As Double
	ov As Short				'Original vertex
End Type

Type Primitive
	kind As UByte
	vp(0 To 11) As Short	'Vertex pointers
	m(0 To 15) As Short		'Materials (paint jobs)
	fcull As ULong			'Front culling flags
	bcull As ULong			'Back culling flags
	flags As UByte			'For 1-sidedness and Z-bias
	sel As Byte				'Selected or similar
End Type

Type VisualPrimitive
	kind As UByte
	vp(0 To 2) As Short
	op As Short				'Original primitive
	m As Short
	flags As UByte
	sel As Byte				'Selected or similar
End Type

Type Shape3D
	vs As Short				'Number of vertices
	v(0 To 511) As Vertex
	ps As Short				'Number of primitives
	p(0 To 511) As Primitive
End Type

Type VisualShape3D
	vs As Short
	v(0 To 1023) As VisualVertex
	ps As Short
	p(0 To 1023) As VisualPrimitive
	os As UByte		'Original shape (car 0 to exp 3)
End Type

Type Window3D
	x1 As UShort	'Screen-based coordiantes
	y1 As UShort
	x2 As UShort
	y2 As UShort
	xc As UShort	'Centre
	yc As UShort
	zoom As Double	'In pixels per meter
	active As Byte
	title As String
End Type

Type StuntsMaterial
	c As ULong		'Base colour
	g As Byte		'Grille type
End Type

'Car models are 0 to 2 for the usual car0 to car2
'Models 3 and 4 are auxiliary
Dim Shared car(0 To 4) As Shape3D, cexp(0 To 3) As Shape3D
Dim Shared carclip As Shape3D
Dim Shared paintjobs As UByte = 7, curpj As UByte
Dim Shared carscale(0 To 3) As Double => {1 / 500, 1 / 25, 1 / 20, 1 / 1600}
Dim Shared vcar As VisualShape3D, wfcar As Shape3D
Dim Shared w3d(1 To 4) As Window3D, curmodel As Byte
Dim Shared material(0 To 255) As StuntsMaterial
Dim Shared pjcol(1 To 32, 1 To 4) As UByte, pjcols As UByte
Dim Shared curyaw As Double = PI + PI / 4, curpitch As Double = PI / 8

Declare Sub GenerateWheels (where As Byte = curmodel)

Type ByteCoor2D
	x As UByte
	y As UByte
End Type

Type IntCoor2D
	x As UShort
	y As UShort
End Type

Type IntCoor3D
	x As Short
	y As Short
	z As Short
End Type

Union Parameters Field = 1
	Type Field = 1
		b(0 To 305) As UByte
	End Type
	Type Field = 1
		w(0 To 153) As UShort
	End Type
	Type Field = 1
		gears As UByte
		purple As UByte		'Unknown, usually 0
		mass As UShort
		brakes As UShort
		idleRPM As UShort
		dsRPM As UShort		'Downshift RPM
		usRPM As UShort		'Upshift RPM
		maxRPM As UShort
		yellow As UShort	'Unknown, usually 0
		gear(1 To 6) As UShort	'Gear ratios
		knobh As UShort		'Suspected horizotal gear tabulation
		knobv As UShort		'Gear centre line or axis
		knob(1 To 6) As IntCoor2D
		aero As UShort		'Aerodynamic resistance
		torque(0 To 103) As UByte	'Torque curve (torque(0) = idle)
		blue As UShort		'Unknown, usually 0
		grip As UShort
		red(1 To 7) As UShort	'Unknown, usually 238, 184, 0, 32, 16, 12, 8
		airgrip As UShort
		sgrip(1 To 9) As UShort	'Surface grip modifiers
		box(1 To 4) As UShort	'Dimensions for car-car collisions
		height As UShort		'Cockpit height
		wheel(1 To 4) As IntCoor3D	'Wheel coordinates
		steer(0 To 30) As ByteCoor2D	'Steering wheel
		spdneedle As IntCoor2D
		spdnum As UShort		'Number of needle positions
		spd(0 To 103) As ByteCoor2D
		tacneedle As IntCoor2D
		tacnum As UShort
		tac(0 To 127) As ByteCoor2D
		null As ULong
	End Type
End Union

Type ParamInput
	xtitle As Short
	ytitle As Short
	title As String
	xvalue As Short
	yvalue As Short
	twidth As Short		'Target width in characters
	min As Long
	max As Long
	ref As Short		'Byte position in SIMD or -1
	kind As Byte		'1 for byte, 2 for word
	unit As String
	multi As Single		'Multiplier to get to unit
	value As Long		'Value if not in SIMD
	opt As Short		'First text option in array if not a number
End Type


Dim Shared par As Parameters, carinfo As String
Dim Shared carname As String, carsignname As String
Dim Shared ptarget(1 To 100) As ParamInput
Dim Shared textoption(0 To 4) As String


Type Bitmap
	id As String			'Chunk ID, image name
	w As UShort				'Width
	h As UShort				'Height
	uw(1 To 2) As Short		'Unknown words
	x As UShort				'X offset (like in the VSH)
	y As UShort				'Y offset (like in the VSH)
	rx As UShort			'Real X offset
	ry As UShort			'Real Y offset
	ub(1 To 4) As UByte		'Unknown bytes
	i As UByte Ptr			'Original image
	i2 As UByte Ptr			'2x 32bpp image
	i3 As ULong Ptr			'3X 32bpp image
	ismask As Byte
	hasmask As Byte
	maskname As String
End Type

Type DashItem
	iname As String
	id As String
	n As Byte
End Type

Type Colour Field = 1
	Union
		Type
			b As UByte
			g As UByte
			r As UByte
			a As UByte
		End Type
		c As ULong
	End Union
End Type

Dim Shared bitmap(0 To 100) As Bitmap, bitmaps As Byte = 0
Dim Shared pal(0 To 255) As Colour, dashitem(1 To 15) As DashItem
Dim Shared opmode As UByte = 0
Const maskmask As String = "/dasm/inm1/inm3/dota/gnab/"
Const masked As String = "/dast:dasm/ins1:inm1/ins3:inm3/dot :dota/gnob:gnab/"


Type ListedFile
	name As String
	title As String
	type As UByte
End Type

Dim Shared config_id(1 To 100) As String, config_value(1 To 100) As String
Dim Shared configs As Short, workdir As String, progdir As String
Dim Shared lfile(1 To 200) As ListedFile, lfiles As Short
Dim Shared File_File As String, File_Error As String, File_ErNum As Byte


Type InputValue
	s As String
	v As Double
	allowed As String
	min As Double
	max As Double
	maxlength As UByte
	fcol As ULong
	bcol As ULong
	x As Short
	y As Short
	init As Byte
	fpt As Byte
	cursor As UByte
End Type


Type BlissButton
	x As Short
	y As Short
	w As Short
	h As Short
	text As String
	fcol As ULong	'Normal foreground colour
	bcol As ULong	'Normal background colour
	icol As ULong	'Foreground colour when inactive
	v As Short		'Value it returns when clicked on
	status As Byte	'Status so far
End Type

Dim Shared button(1 To 20) As BlissButton, buttons As Byte

Dim Shared manual_line As Long = 1


Sub CreateSimpleButton (x As Short, y As Short, text As String, v As Short)
	buttons += 1
	button(buttons).x = x
	button(buttons).y = y
	button(buttons).h = 32
	button(buttons).w = 8 * Len(text) + 32
	button(buttons).text = text
	button(buttons).v = v
	button(buttons).fcol = GRAY_COL
	button(buttons).icol = H_COL
	button(buttons).bcol = B_COL
	button(buttons).status = 0
End Sub


Sub AppendButton (text As String, v As Short, direc As Byte = 0)
	buttons += 1
	If direc Then
		button(buttons).x = button(buttons - 1).x
		button(buttons).y = button(buttons - 1).y + button(buttons - 1).h + 8
		button(buttons).w = button(buttons - 1).w
	Else
		button(buttons).x = button(buttons - 1).x + button(buttons - 1).w + 16
		button(buttons).y = button(buttons - 1).y
		button(buttons).w = 8 * Len(text) + 32
	End If
	button(buttons).text = text
	button(buttons).h = 32
	button(buttons).v = v
	button(buttons).fcol = button(buttons - 1).fcol
	button(buttons).icol = button(buttons - 1).icol
	button(buttons).bcol = button(buttons - 1).bcol
	button(buttons).status = 0
End Sub


Function MaintainButtons As Short
	Dim As Integer xm, ym, bm
	Dim i As Short, hover As Byte, shoulddraw As Byte
	Dim v As Short
	Static As Integer exxm, exym, exbm
	
	GetMouse xm, ym, , bm
	If exxm = xm AndAlso exym = ym AndAlso exbm = bm Then Return 0

	v = 0
	For i = 1 To buttons
		If xm >= button(i).x AndAlso ym >= button(i).y _
			AndAlso xm < button(i).x + button(i).w _
			AndAlso ym < button(i).y + button(i).h Then
		
			hover = -1
		Else
			hover = 0
		End If
		
		shoulddraw = 0
		
		'Check previous button status
		Select Case button(i).status
			Case 0	'Never drawn
				'Should be drawn because it was never
				'drawn so far
				shoulddraw = -1
				If hover Then
					button(i).status = 2
				Else
					button(i).status = 1
				End If
			Case 1	'Not being hovered
				If hover Then
					button(i).status = 2
					shoulddraw = -1
				End If
			Case 2	'Being hovered
				If Not hover Then
					button(i).status = 1
					shoulddraw = -1
				End If
			Case 3	'Inactive
				'Assumed to have been drawn already
			Case -3	'Waiting to become inactive
				'Should be drawn as inactive
				button(i).status = 3
				shoulddraw = -1
		End Select
		
		If shoulddraw Then
			Select Case button(i).status
				Case 1	'Active, not being hovered
					Line (button(i).x, button(i).y)- Step(button(i).w - 1, button(i).h - 1), button(i).fcol, B
					Line (button(i).x + 1, button(i).y + 1)- Step(button(i).w - 3, button(i).h - 3), button(i).fcol, B
					Line (button(i).x + 2, button(i).y + 2)- Step(button(i).w - 5, button(i).h - 5), button(i).bcol, BF
					Draw String (button(i).x + button(i).w \ 2 - Len(button(i).text) * 4, _
							button(i).y + button(i).h \ 2 - 8), button(i).text, button(i).fcol
				Case 2	'Active, hovered
					Line (button(i).x, button(i).y)- Step(button(i).w - 1, button(i).h - 1), button(i).fcol, BF
					Draw String (button(i).x + button(i).w \ 2 - Len(button(i).text) * 4, _
							button(i).y + button(i).h \ 2 - 8), button(i).text, button(i).bcol
				Case 3	'Inactive
					Line (button(i).x, button(i).y)- Step(button(i).w - 1, button(i).h - 1), button(i).icol, B
					Line (button(i).x + 1, button(i).y + 1)- Step(button(i).w - 3, button(i).h - 3), button(i).icol, B
					Line (button(i).x + 2, button(i).y + 2)- Step(button(i).w - 5, button(i).h - 5), button(i).bcol, BF
					Draw String (button(i).x + button(i).w \ 2 - Len(button(i).text) * 4, _
							button(i).y + button(i).h \ 2 - 8), button(i).text, button(i).icol
			End Select
		End If
		
		If hover <> 0 AndAlso bm = 1 Then v = button(i).v
	Next i
	
	Return v
End Function


Sub InitDashItems
	dashitem(1).iname = "Dashboard"
	dashitem(1).id = "dash"
	dashitem(1).n = 0
	
	dashitem(2).iname = "Dashboard top"
	dashitem(2).id = "dast"
	dashitem(2).n = 0
	
	dashitem(3).iname = "Roof"
	dashitem(3).id = "roof"
	dashitem(3).n = 0
	
	dashitem(4).iname = "Steering wheel"
	dashitem(4).id = "whl2"
	dashitem(4).n = 0
	
	dashitem(5).iname = "Steering (left)"
	dashitem(5).id = "whl1"
	dashitem(5).n = 0
	
	dashitem(6).iname = "Steering (right)"
	dashitem(6).id = "whl3"
	dashitem(6).n = 0
	
	dashitem(7).iname = "Instruments"
	dashitem(7).id = "ins2"
	dashitem(7).n = 0
	
	dashitem(8).iname = "Inst. (left)"
	dashitem(8).id = "ins1"
	dashitem(8).n = 0
	
	dashitem(9).iname = "Inst. (right)"
	dashitem(9).id = "ins3"
	dashitem(9).n = 0
	
	dashitem(10).iname = "Gear box"
	dashitem(10).id = "gbox"
	dashitem(10).n = 0
	
	dashitem(11).iname = "Gear knob"
	dashitem(11).id = "gnob"
	dashitem(11).n = 0
	
	dashitem(12).iname = "Steer dot"
	dashitem(12).id = "dot "
	dashitem(12).n = 0
	
	dashitem(13).iname = "Steer dot I"
	dashitem(13).id = "dot1"
	dashitem(13).n = 0
	
	dashitem(14).iname = "Steer dot II"
	dashitem(14).id = "dot2"
	dashitem(14).n = 0
	
	dashitem(15).iname = "Speed digit 0"
	dashitem(15).id = "dig0"
	dashitem(15).n = 0
	
	'These allow for moving ins1/ins3 bitmaps in the
	'event that no ins2 has been loaded. They could
	'also be useful for a background image
	bitmap(0).x = 0
	bitmap(0).y = 0
	bitmap(0).rx = 0
	bitmap(0).ry = 0
	bitmap(0).w = 320
	bitmap(0).h = 200
End Sub


Sub InitParamTargets
	ptarget(1).title = "Mass"
	ptarget(1).xtitle = 25
	ptarget(1).ytitle = 102
	ptarget(1).xvalue = 25 + 96
	ptarget(1).yvalue = 102
	ptarget(1).kind = 2
	ptarget(1).ref = 2
	ptarget(1).min = 0
	ptarget(1).max = 65535
	ptarget(1).unit = "kg"
	ptarget(1).multi = 43.7
	ptarget(1).twidth = 30
	
	ptarget(2).title = "Powergear"
	ptarget(2).xtitle = 25
	ptarget(2).ytitle = 118
	ptarget(2).xvalue = 25 + 96
	ptarget(2).yvalue = 118
	ptarget(2).ref = -1
	ptarget(2).kind = 0
	ptarget(2).min = 0
	ptarget(2).max = 4
	ptarget(2).opt = 0
	ptarget(2).twidth = 30
	textoption(0) = "Flexible"
	textoption(1) = "Rigid"
	textoption(2) = "Anti-PG"
	textoption(3) = "None"
	textoption(4) = "No (bug-free)"
	
	ptarget(3).title = "Brakes"
	ptarget(3).xtitle = 25
	ptarget(3).ytitle = 134
	ptarget(3).xvalue = 25 + 96
	ptarget(3).yvalue = 134
	ptarget(3).kind = 2
	ptarget(3).ref = 4
	ptarget(3).min = 0
	ptarget(3).max = 65535
	ptarget(3).twidth = 30
	
	ptarget(4).title = "Air drag"
	ptarget(4).xtitle = 25
	ptarget(4).ytitle = 150
	ptarget(4).xvalue = 25 + 96
	ptarget(4).yvalue = 150
	ptarget(4).kind = 2
	ptarget(4).ref = 56
	ptarget(4).min = 0
	ptarget(4).max = 65535
	ptarget(4).twidth = 30
	
	ptarget(5).title = "C. height"
	ptarget(5).xtitle = 25
	ptarget(5).ytitle = 166
	ptarget(5).xvalue = 25 + 96
	ptarget(5).yvalue = 166
	ptarget(5).kind = 2
	ptarget(5).ref = 208
	ptarget(5).min = 0
	ptarget(5).max = 65535
	ptarget(5).twidth = 30
	
	ptarget(6).title = "Intrinsic"
	ptarget(6).xtitle = 335
	ptarget(6).ytitle = 70
	ptarget(6).xvalue = 335 + 104
	ptarget(6).yvalue = 70
	ptarget(6).kind = 2
	ptarget(6).ref = 164
	ptarget(6).min = 0
	ptarget(6).max = 65535
	ptarget(6).twidth = 20
	
	ptarget(7).title = "On asphalt"
	ptarget(7).xtitle = 335
	ptarget(7).ytitle = 86
	ptarget(7).xvalue = 335 + 104
	ptarget(7).yvalue = 86
	ptarget(7).kind = 2
	ptarget(7).ref = 182
	ptarget(7).min = 0
	ptarget(7).max = 65535
	ptarget(7).twidth = 20
	
	ptarget(8).title = "On dirt"
	ptarget(8).xtitle = 335
	ptarget(8).ytitle = 102
	ptarget(8).xvalue = 335 + 104
	ptarget(8).yvalue = 102
	ptarget(8).kind = 2
	ptarget(8).ref = 184
	ptarget(8).min = 0
	ptarget(8).max = 65535
	ptarget(8).twidth = 20
	
	ptarget(9).title = "On ice"
	ptarget(9).xtitle = 335
	ptarget(9).ytitle = 118
	ptarget(9).xvalue = 335 + 104
	ptarget(9).yvalue = 118
	ptarget(9).kind = 2
	ptarget(9).ref = 186
	ptarget(9).min = 0
	ptarget(9).max = 65535
	ptarget(9).twidth = 20
	
	ptarget(10).title = "On grass"
	ptarget(10).xtitle = 335
	ptarget(10).ytitle = 134
	ptarget(10).xvalue = 335 + 104
	ptarget(10).yvalue = 134
	ptarget(10).kind = 2
	ptarget(10).ref = 188
	ptarget(10).min = 0
	ptarget(10).max = 65535
	ptarget(10).twidth = 20
	
	ptarget(11).title = "Airborne"
	ptarget(11).xtitle = 335
	ptarget(11).ytitle = 150
	ptarget(11).xvalue = 335 + 104
	ptarget(11).yvalue = 150
	ptarget(11).kind = 2
	ptarget(11).ref = 180
	ptarget(11).min = 0
	ptarget(11).max = 65535
	ptarget(11).twidth = 20
	
	ptarget(12).title = "Gears"
	ptarget(12).xtitle = 545
	ptarget(12).ytitle = 70
	ptarget(12).xvalue = 545 + 104
	ptarget(12).yvalue = 70
	ptarget(12).kind = 1
	ptarget(12).ref = 0
	ptarget(12).min = 0
	ptarget(12).max = 6
	ptarget(12).twidth = 20
	
	For i As Short = 0 To 5
		ptarget(13 + i).title = (i + 1) & "th ratio"
		ptarget(13 + i).xtitle = 545
		ptarget(13 + i).ytitle = 86 + 16 * i
		ptarget(13 + i).xvalue = 545 + 104
		ptarget(13 + i).yvalue = 86 + 16 * i
		ptarget(13 + i).kind = 2
		ptarget(13 + i).ref = 16 + 2 * i
		ptarget(13 + i).min = 0
		ptarget(13 + i).max = 65535
		ptarget(13 + i).twidth = 20
	Next i
	ptarget(13).title = "1st ratio"
	ptarget(14).title = "2nd ratio"
	
	ptarget(19).title = "Purple"
	ptarget(19).xtitle = 755
	ptarget(19).ytitle = 70
	ptarget(19).xvalue = 755 + 104
	ptarget(19).yvalue = 70
	ptarget(19).kind = 1
	ptarget(19).ref = 1
	ptarget(19).min = 0
	ptarget(19).max = 255
	ptarget(19).twidth = 20
	
	ptarget(20).title = "Yellow"
	ptarget(20).xtitle = 755
	ptarget(20).ytitle = 86
	ptarget(20).xvalue = 755 + 104
	ptarget(20).yvalue = 86
	ptarget(20).kind = 2
	ptarget(20).ref = 14
	ptarget(20).min = 0
	ptarget(20).max = 65535
	ptarget(20).twidth = 20
	
	ptarget(21).title = "Blue"
	ptarget(21).xtitle = 755
	ptarget(21).ytitle = 102
	ptarget(21).xvalue = 755 + 104
	ptarget(21).yvalue = 102
	ptarget(21).kind = 2
	ptarget(21).ref = 162
	ptarget(21).min = 0
	ptarget(21).max = 65535
	ptarget(21).twidth = 20
	
	For i As Short = 0 To 6
		ptarget(22 + i).title = "Red #" & (i + 1)
		ptarget(22 + i).xtitle = 755
		ptarget(22 + i).ytitle = 118 + 16 * i
		ptarget(22 + i).xvalue = 755 + 104
		ptarget(22 + i).yvalue = 118 + 16 * i
		ptarget(22 + i).kind = 2
		ptarget(22 + i).ref = 166 + 2 * i
		ptarget(22 + i).min = 0
		ptarget(22 + i).max = 65535
		ptarget(22 + i).twidth = 20
	Next i
	ptarget(26).title = "Needle col."
	
	For i As Short = 0 To 4
		ptarget(29 + i).title = "Orange #" & (i + 1)
		ptarget(29 + i).xtitle = 755
		ptarget(29 + i).ytitle = 230 + 16 * i
		ptarget(29 + i).xvalue = 755 + 104
		ptarget(29 + i).yvalue = 230 + 16 * i
		ptarget(29 + i).kind = 2
		ptarget(29 + i).ref = 190 + 2 * i
		ptarget(29 + i).min = 0
		ptarget(29 + i).max = 65535
		ptarget(29 + i).twidth = 20
	Next i
	
	ptarget(34).title = "Car name"
	ptarget(34).xtitle = 25
	ptarget(34).ytitle = 70
	ptarget(34).xvalue = 25 + 96
	ptarget(34).yvalue = 70
	ptarget(34).kind = 3
	ptarget(34).ref = 208
	ptarget(34).min = 0
	ptarget(34).max = 21	'Max length, in this case
	ptarget(34).twidth = 30
	
	ptarget(35).title = "Sign name"
	ptarget(35).xtitle = 25
	ptarget(35).ytitle = 86
	ptarget(35).xvalue = 25 + 96
	ptarget(35).yvalue = 86
	ptarget(35).kind = 4
	ptarget(35).ref = 208
	ptarget(35).min = 0
	ptarget(35).max = 4		'Max length, in this case
	ptarget(35).twidth = 30
	
	'-------- RPM parameters
	ptarget(36).title = "Idle RPM"
	ptarget(36).xtitle = 755
	ptarget(36).ytitle = 70
	ptarget(36).xvalue = 755 + 120
	ptarget(36).yvalue = 70
	ptarget(36).kind = 2
	ptarget(36).ref = 6
	ptarget(36).min = 0
	ptarget(36).max = 65535
	ptarget(36).twidth = 25
	
	ptarget(37).title = "Downshift RPM"
	ptarget(37).xtitle = 755
	ptarget(37).ytitle = 86
	ptarget(37).xvalue = 755 + 120
	ptarget(37).yvalue = 86
	ptarget(37).kind = 2
	ptarget(37).ref = 8
	ptarget(37).min = 0
	ptarget(37).max = 65535
	ptarget(37).twidth = 25
	
	ptarget(38).title = "Upshift RPM"
	ptarget(38).xtitle = 755
	ptarget(38).ytitle = 102
	ptarget(38).xvalue = 755 + 120
	ptarget(38).yvalue = 102
	ptarget(38).kind = 2
	ptarget(38).ref = 10
	ptarget(38).min = 0
	ptarget(38).max = 65535
	ptarget(38).twidth = 25

	ptarget(39).title = "Maximum RPM"
	ptarget(39).xtitle = 755
	ptarget(39).ytitle = 118
	ptarget(39).xvalue = 755 + 120
	ptarget(39).yvalue = 118
	ptarget(39).kind = 2
	ptarget(39).ref = 12
	ptarget(39).min = 0
	ptarget(39).max = 65535
	ptarget(39).twidth = 25
	
	carname = "New car"
	carsignname = "New!"
	carinfo = "New car]"
	par.mass = 20
	par.brakes = 300
	par.aero = 30
	par.height = 18
	par.grip = 400
	par.airgrip = 0
	par.sgrip(1) = 256
	par.sgrip(2) = 192
	par.sgrip(3) = 64
	par.sgrip(4) = 128
	par.gears = 5
	par.gear(1) = 30000
	par.gear(2) = 20000
	par.gear(3) = 15000
	par.gear(4) = 10000
	par.gear(5) = 5000
	par.gear(6) = 0
	par.purple = 0
	par.yellow = 0
	par.blue = 0
	par.red(1) = 238
	par.red(2) = 184
	par.red(3) = 0
	par.red(4) = 32
	par.red(5) = 16
	par.red(6) = 12
	par.red(7) = 8
	par.sgrip(5) = 0
	par.sgrip(6) = 32
	par.sgrip(7) = 192
	par.sgrip(8) = 128
	par.sgrip(9) = 64
	par.idleRPM = 1000
	par.dsRPM = 2000
	par.usRPM = 9000
	par.maxRPM = 10000
End Sub


Sub Init3DWindows
	w3d(1).x1 = 20 : w3d(1).y1 = 50
	w3d(1).x2 = 370 : w3d(1).y2 = 300
	w3d(1).xc = 195 : w3d(1).yc = 175
	w3d(1).zoom = 40
	w3d(1).active = -1 : w3d(1).title = "Top"
	
	w3d(2).x1 = 380 : w3d(2).y1 = 50
	w3d(2).x2 = 730 : w3d(2).y2 = 300
	w3d(2).xc = 555 : w3d(2).yc = 275
	w3d(2).zoom = 80
	w3d(2).active = -1 : w3d(2).title = "Front"
	
	w3d(3).x1 = 20 : w3d(3).y1 = 315
	w3d(3).x2 = 370 : w3d(3).y2 = 565
	w3d(3).xc = 195 : w3d(3).yc = 540
	w3d(3).zoom = 60
	w3d(3).active = -1 : w3d(3).title = "Side"
	
	w3d(4).x1 = 380 : w3d(4).y1 = 315
	w3d(4).x2 = 730 : w3d(4).y2 = 565
	w3d(4).xc = 555 : w3d(4).yc = 440
	w3d(4).zoom = 60
	w3d(4).active = -1 : w3d(4).title = "3D view"
End Sub


Sub LoadMaterials
	Dim f As Integer
	
	f = FreeFile
	
	If FileExists(progdir & "material.dat") Then
		material(200).c = &HFF804000
		material(201).c = &HFF904800
		material(202).c = &HFFA05000
		material(203).c = &HFFB05800
		
		Open progdir & "material.dat" For Binary Access Read As f
		For i As Short = 0 To LoF(f) \ 4 - 1
			Get #f, , material(i).c
			material(i).g = material(i).c ShR 24
			material(i).c Or= &HFF000000ul
		Next i
		Close f
	End If
	
	If FileExists("pjobs.cfg") Then
		Dim s As String, n As Short, i As Byte
		
		Open "pjobs.cfg" For Input As f
		While Not EoF(f)
			If pjcols = 32 Then Exit While
			
			Line Input #f, s
			
			s = Trim(s)
			n = InStr(s, ";")
			If n Then s = RTrim(Left(s, n - 1))
			
			If Len(s) Then
				pjcols += 1
				For i = 1 To 4
					pjcol(pjcols, i) = ValInt(s)
					n = InStr(s, " ")
					If n Then
						s = LTrim(Mid(s, n + 1))
					Else
						s = ""
					End If
				Next i
			End If
		WEnd
		Close f
	End If
End Sub


Sub SaveOBJ (finame As String, which As Byte = curmodel)
	Dim f As Integer, vv(0 To 2047) As Byte, vvs As Short
	Dim vtrans(0 To 2047) As Short, n As Short
	
	f = FreeFile
	File_File = finame
	Open finame For Output As f
	If Err Then
		File_ErNum = 2
		File_Error = "Invalid file name"
		Exit Sub
	Else
		File_ErNum = 0
	End If
		
	'Mark vertices that correspond to primitives that
	'can be put in an OBJ file
	For i As Short = 0 To car(which).ps - 1
		If car(which).p(i).kind >= 3 And car(which).p(i).kind <= 10 Then
			For j As Byte = 0 To car(which).p(i).kind - 1
				vv(car(which).p(i).vp(j)) = -1
			Next j
		End If
	Next i
	
	'Give them numbers counting only the ones that will
	'appear in the OBJ
	n = 1
	For i As Short = 0 To car(which).vs - 1
		If vv(i) Then
			vtrans(i) = n
			n += 1
		End If
	Next i
	
	'Refer to Stunts material library
	Print #f, "mtllib stunts.mtl"
	
	'Now store the vertices
	For i As Short = 0 To car(which).vs - 1
		If vv(i) Then _
			Print #f, "v " & car(which).v(i).x & " " & car(which).v(i).z & " " & -car(which).v(i).y	'Negative for chirality
	Next i
	
	'Then store primitives with current paint job
	For i As Short = 0 To car(which).ps - 1
		If car(which).p(i).kind >= 3 And car(which).p(i).kind <= 10 Then
			If car(which).p(i).m(curpj) >= 200 And car(which).p(i).m(curpj) <= 203 Then
				Print #f, "usemtl base" & (car(which).p(i).m(curpj) - 199)
			Else
				Print #f, "usemtl Stunts" & car(which).p(i).m(curpj)
			End If
			Print #f, "f";
			For j As Byte = 0 To car(which).p(i).kind - 1
				Print #f, " " & vtrans(car(which).p(i).vp(j));
			Next j
			Print #f,
		End If
	Next i
	
	Close f
End Sub


Sub LoadOBJ (finame As String, where As Byte = curmodel)
	File_File = finame
	File_ErNum = 0
	If Not FileExists(finame) Then
		File_Error = "File not found"
		File_ErNum = 1
		Exit Sub
	End If
	
	Dim f As Integer, s As String, n As Short, c As String
	Dim mat As Short = 200
	
	f = FreeFile
	
	car(where).ps = 4	'Initialise to 4 wheels only
	car(where).vs = 32	'8 for bounding box plus 24 for wheels	
	
	'Borrow existing wheels if any. Otherwise, generate a set
	If car(where).p(0).kind <> 12 Or car(where).p(1).kind <> 12 Or _
		car(where).p(2).kind <> 12 Or car(where).p(3).kind <> 12 Then
	
		GenerateWheels where
	End If
		
		
	Open finame For Input As f
	
	Do While Not EoF(f)
		Line Input #f, s
		
		'Remove comments
		n = InStr(s, "#")
		If n Then s = RTrim(Left(s, n - 1))
		
		'Separate command from arguments
		n = InStr(s, " ")
		If n Then
			c = RTrim(Left(s, n - 1))
			s = LTrim(Mid(s, n + 1))
		Else
			c = s
			s = ""
		End If
		c = LCase(c)
		
		Select Case c
			Case "v"	'Load a vertex
				car(where).vs += 1
				car(where).v(car(where).vs - 1).x = Val(s)
				n = InStr(s, " ")
				If n Then s = LTrim(Mid(s, n + 1)) Else s = ""
				car(where).v(car(where).vs - 1).z = Val(s)
				n = InStr(s, " ")
				If n Then s = LTrim(Mid(s, n + 1)) Else s = ""
				car(where).v(car(where).vs - 1).y = -Val(s)	'Negative for chirality
			Case "f"	'Load a face
				Dim i As Byte
				
				car(where).ps += 1
				For i = 0 To 9
					If Len(s) = 0 Then Exit For
					car(where).p(car(where).ps - 1).vp(i) = ValInt(s) + 31
					n = InStr(s, " ")
					If n Then s = LTrim(Mid(s, n + 1)) Else s = ""
				Next i
				
				If i Then
					car(where).p(car(where).ps - 1).kind = i
					car(where).p(car(where).ps - 1).bcull = -1
					car(where).p(car(where).ps - 1).fcull = -1
					car(where).p(car(where).ps - 1).flags = 1
					car(where).p(car(where).ps - 1).sel = 0
					For j As Byte = 0 To paintjobs - 1
						car(where).p(car(where).ps - 1).m(j) = mat
					Next j
				Else
					car(where).ps -= 1
				End If
			Case "usemtl"
				s = LCase(s)
				If Left(s, 6) = "stunts" Then
					mat = ValInt(Mid(s, 7))
				ElseIf Left(s, 4) = "base" Then
					mat = 200 + ValInt(Mid(s, 5))
				Else
					mat = 200
				End If
		End Select
	Loop
	
	Close f
	
	'At this point, the car bouding box should be set
	GenerateBoundingBox where
End Sub


Sub Load3SH (finame As String, which As UShort = -1)
	File_File = finame
	File_ErNum = 0
	If Not FileExists(finame) Then
		File_Error = "File not found"
		File_ErNum = 1
		Exit Sub
	End If
	
	Dim f As Integer, id(1 To 16) As String
	Dim resp(1 To 16) As ULong, ress As Short
	Dim mybyte As UByte, myword As UShort, myint As Short
	Dim localpjs As UByte, n As Short
	
	f = FreeFile
	Open finame For Binary Access Read As f
	
	Get #f, 5, ress
	For i As Short = 1 To ress
		If i > 16 Then Exit For
		id(i) = Space(4)
		Get #f, 7 + 4 * (i - 1), id(i)
		Get #f, 7 + 4 * ress + 4 * (i - 1), resp(i)
		resp(i) += 7 + 8 * ress
	Next i
	If ress > 16 Then ress = 16
	
	'Load car shapes
	For i As Byte = 0 To 2
		'Find the "cari" shape
		For j As Byte = 1 To 16
			If id(j) = "car" & i Then
				Seek #f, resp(j)
				Exit For
			End If
		Next j
		
		'Only load it if flagged
		If which And (2 ^ i) Then
			Get #f, , mybyte	'Get num. of vertices
			car(i).vs = mybyte
			Get #f, , mybyte	'Get num. of primitives
			car(i).ps = mybyte
			Get #f, , localpjs	'Get num. of paint-jobs
			Get #f, , mybyte	'Skip a byte
			
			'Load vertices
			For j As Short = 0 to car(i).vs - 1
				Get #f, , myint
				car(i).v(j).x = carscale(i) * myint
				Get #f, , myint
				car(i).v(j).z = carscale(i) * myint
				Get #f, , myint
				car(i).v(j).y = -carscale(i) * myint		'Negative for chirality
			Next j
			
			'Load front culling
			For j As Short = 0 To car(i).ps - 1
				Get #f, , car(i).p(j).fcull
			Next j
			
			'Load back culling
			For j As Short = 0 To car(i).ps - 1
				Get #f, , car(i).p(j).bcull
			Next j
			
			'Load all other primitive data
			For j As Short = 0 To car(i).ps - 1
				Get #f, , car(i).p(j).kind
				Get #f, , car(i).p(j).flags
				For k As Byte = 0 To localpjs - 1
					Get #f, , mybyte
					car(i).p(j).m(k) = mybyte
				Next k
				
				'See how many vertices for this primitive
				Select Case car(i).p(j).kind
					Case 1 To 10 : n = car(i).p(j).kind
					Case 11 : n = 2
					Case 12 : n = 6
					Case Else : n = 0
				End Select
				
				'Load vertex pointers (check: 0-based or 1-based)
				For k As Short = 0 To n - 1
					Get #f, , mybyte
					car(i).p(j).vp(k) = mybyte
				Next k
			Next j
		End If
	Next i
	
	'Load exp shapes
	For i As Byte = 0 To 3
		'Find the "expi" shape
		For j As Byte = 1 To 16
			If id(j) = "exp" & i Then
				Seek #f, resp(j)
				Exit For
			End If
		Next j
		
		'Only load it if flagged
		If which And (2 ^ (i + 3)) Then
			Get #f, , mybyte	'Get num. of vertices
			cexp(i).vs = mybyte
			Get #f, , mybyte	'Get num. of primitives
			cexp(i).ps = mybyte
			Get #f, , localpjs	'Get num. of paint-jobs
			Get #f, , mybyte	'Skip a byte
			
			'Load vertices
			For j As Short = 0 to cexp(i).vs - 1
				Get #f, , myint
				cexp(i).v(j).x = carscale(1) * myint
				Get #f, , myint
				cexp(i).v(j).z = carscale(1) * myint
				Get #f, , myint
				cexp(i).v(j).y = carscale(1) * myint
			Next j
			
			'Load front culling
			For j As Short = 0 To cexp(i).ps - 1
				Get #f, , cexp(i).p(j).fcull
			Next j
			
			'Load back culling
			For j As Short = 0 To cexp(i).ps - 1
				Get #f, , cexp(i).p(j).bcull
			Next j
			
			'Load all other primitive data
			For j As Short = 0 To cexp(i).ps - 1
				Get #f, , cexp(i).p(j).kind
				Get #f, , cexp(i).p(j).flags
				For k As Byte = 0 To localpjs - 1
					Get #f, , mybyte
					cexp(i).p(j).m(k) = mybyte
				Next k
				
				'See how many vertices for this primitive
				Select Case cexp(i).p(j).kind
					Case 1 To 10 : n = cexp(i).p(j).kind
					Case 11 : n = 2
					Case 12 : n = 6
					Case Else : n = 0
				End Select
				
				'Load vertex pointers (check: 0-based or 1-based)
				For k As Short = 0 To n - 1
					Get #f, , mybyte
					cexp(i).p(j).vp(k) = mybyte
				Next k
			Next j
		End If
	Next i
	
	Close f
	
	'I have no idea why I chose to use a local paint jobs
	'variable, but I set it to equal
	paintjobs = localpjs
End Sub


Sub Save3SH (finame As String)
	Dim f As Integer, shape As Shape3D
	Dim p As ULong, scale As Double, n As Short
	
	f = FreeFile
	File_File = finame
	Open finame For Output As f
	If Err Then
		File_ErNum = 2
		File_Error = "Invalid file name"
		Exit Sub
	Else
		File_ErNum = 0
		Close f
	End If
	Open finame For Binary Access Write As f
	
	Put #f, 5, MkShort(7)
	Put #f, , "car0car1car2exp0exp1exp2exp3"
	Seek #f, Seek(f) + 28
	
	For i As Byte = 0 To 6
		'Copy the model to store to shape
		If i < 3 Then
			shape = car(i)
			scale = carscale(i)
		Else
			shape = cexp(i - 3)
			scale = carscale(1)
		End If
		
		'Store pointer to chunk
		p = Seek(f)
		Put #f, 35 + 4 * i, MkL(p - 63)
		Seek #f, p
		
		'Store shape info
		Put #f, , Chr(shape.vs)
		Put #f, , Chr(shape.ps)
		Put #f, , paintjobs
		Put #f, , Chr(0)
		
		'Store vertices
		For j As Short = 0 To shape.vs - 1
			Put #f, , MkShort(shape.v(j).x / scale)
			Put #f, , MkShort(shape.v(j).z / scale)
			Put #f, , MkShort(-shape.v(j).y / scale)	'Negative for chirality
		Next j
		
		'Store culling
		For j As Short = 0 To shape.ps - 1
			Put #f, , shape.p(j).fcull
		Next j
		For j As Short = 0 To shape.ps - 1
			Put #f, , shape.p(j).bcull
		Next j
		
		'Store primitives
		For j As Short = 0 To shape.ps - 1
			Put #f, , shape.p(j).kind
			Put #f, , shape.p(j).flags
			For k As Byte = 0 To paintjobs - 1
				Put #f, , Chr(shape.p(j).m(k))
			Next k
			
			Select Case shape.p(j).kind
				Case 1 To 10 : n = shape.p(j).kind
				Case 11 : n = 2
				Case 12 : n = 6
				Case Else : n = 0
			End Select
			
			For k As Byte = 0 To n - 1
				Put #f, , Chr(shape.p(j).vp(k))
			Next k
		Next j
		
		'Leave some space
		Put #f, , Chr(0, 0, 0)
	Next i
	Put #f, 1, MkL(LoF(f))
	Close f
End Sub


Sub SimplifyModel (n As UByte, pj As UByte = 0)
	'Convert a car model to simpler primitives for easier
	'rendering and detection of primitives and vertices
	
	Dim i As Short, j As Short, r As Short
	
	'Start by copying all vertices
	vcar.vs = car(n).vs
	For i = 0 To car(n).vs - 1
		vcar.v(i).x = car(n).v(i).x
		vcar.v(i).y = car(n).v(i).y
		vcar.v(i).z = car(n).v(i).z
		vcar.v(i).ov = i	'First vertices are original
	Next i
	
	'Now process all primitives. Wheels first
	vcar.ps = 0
	For i = 0 To 3
		'Here, wheel shapes should be generated
	Next i
	
	'Primitives that are not main wheels come now
	For i = 4 To car(n).ps - 1
		Select Case car(n).p(i).kind
			Case 2, 11	'Segment and sphere
				vcar.p(vcar.ps).kind = car(n).p(i).kind
				vcar.p(vcar.ps).vp(0) = car(n).p(i).vp(0)
				vcar.p(vcar.ps).vp(1) = car(n).p(i).vp(1)
				vcar.p(vcar.ps).flags = car(n).p(i).flags
				vcar.p(vcar.ps).m = car(n).p(i).m(pj)
				vcar.p(vcar.ps).op = i
				vcar.p(vcar.ps).sel = car(n).p(i).sel
				vcar.ps += 1
			Case 3	'Triangle
				vcar.p(vcar.ps).kind = 3
				vcar.p(vcar.ps).vp(0) = car(n).p(i).vp(0)
				vcar.p(vcar.ps).vp(1) = car(n).p(i).vp(1)
				vcar.p(vcar.ps).vp(2) = car(n).p(i).vp(2)
				vcar.p(vcar.ps).flags = car(n).p(i).flags
				vcar.p(vcar.ps).m = car(n).p(i).m(pj)
				vcar.p(vcar.ps).op = i
				vcar.p(vcar.ps).sel = car(n).p(i).sel
				vcar.ps += 1
			Case 4 To 10	'Polygon
				For j = 0 To car(n).p(i).kind - 3
					vcar.p(vcar.ps).kind = 3
					vcar.p(vcar.ps).vp(0) = car(n).p(i).vp(0)
					vcar.p(vcar.ps).vp(1) = car(n).p(i).vp(j + 1)
					vcar.p(vcar.ps).vp(2) = car(n).p(i).vp(j + 2)
					vcar.p(vcar.ps).flags = car(n).p(i).flags
					vcar.p(vcar.ps).m = car(n).p(i).m(pj)
					vcar.p(vcar.ps).op = i
					vcar.p(vcar.ps).sel = car(n).p(i).sel
					vcar.ps += 1
				Next j
			Case 12			'Wheel
		End Select
	Next i
End Sub


Sub LoadRES (finame As String)
	File_File = finame
	File_ErNum = 0
	If Not FileExists(finame) Then
		File_Error = "File not found"
		File_ErNum = 1
		Exit Sub
	End If
	
	Dim f As Integer, numres As Short, id As String
	Dim p As ULong, n As Short
	
	f = FreeFile
	Open finame For Binary Access Read As f
	Get #f, 5, numres
	id = Space(4)
	For i As Short = 1 To numres
		Get #f, 4 * i + 3, id
		Get #f, 4 * i + 4 * numres + 3, p
		p += 8 * numres + 7
		Select Case id
			Case "edes"
				carinfo = Space(1023)
				Get #f, p, carinfo
				n = InStr(carinfo, Chr(0))
				If n Then carinfo = Left(carinfo, n - 1)
			Case "gnam"
				carname = Space(255)
				Get #f, p, carname
				n = InStr(carname, Chr(0))
				If n Then carname = Left(carname, n - 1)
			Case "gsna"
				carsignname = Space(255)
				Get #f, p, carsignname
				n = InStr(carsignname, Chr(0))
				If n Then carsignname = Left(carsignname, n - 1)
			Case "simd"
				Get #f, p, par
		End Select
	Next i
	Close f
	
	UpdatePG 0
	
	If Len(carname) Then
		WindowTitle PROGRAM_TITLE & " - " & carname
	Else
		WindowTitle PROGRAM_TITLE
	End If
End Sub


Sub SaveRES (finame As String)
	Dim f As Integer, curpt As Long
	
	f = FreeFile
	File_File = finame
	Open finame For Output As f
	If Err Then
		File_ErNum = 2
		File_Error = "Invalid file name"
		Exit Sub
	Else
		File_ErNum = 0
		Close f
	End If
	Open finame For Binary Access Write As f
	
	Put #f, 5, MkShort(4)
	Put #f, , "simdgnamgsnaedes" & MkL(0)
	curpt = 39
	
	Put #f, curpt, par
	curpt = Seek(f)
	Put #f, 27, MkL(curpt - 39)
	Put #f, curpt, carname & Chr(0)
	curpt = Seek(f)
	Put #f, 31, MkL(curpt - 39)
	Put #f, curpt, carsignname & Chr(0)
	curpt = Seek(f)
	Put #f, 35, MkL(curpt - 39)
	Put #f, curpt, carinfo & Chr(0)
	
	Put #f, 1, MkL(LoF(f))
	Close f
End Sub


Sub LoadCar (finame As String)
	File_File = finame
	File_ErNum = 0
	If Not FileExists(finame) Then
		File_Error = "File not found"
		File_ErNum = 1
		Exit Sub
	End If
	
	Dim f As Integer, fp(1 To 4) As Long, n As UByte
	Dim s As String, nn As Short, vps As Short, d As Long
	
	'First, clean all image data
	For i As Byte = 1 To bitmaps
		If bitmap(i).i Then Deallocate bitmap(i).i
		If bitmap(i).i2 Then ImageDestroy bitmap(i).i2
		If bitmap(i).i3 Then ImageDestroy bitmap(i).i3
	Next i
	
	f = FreeFile
	Open finame For Binary Access Read As f
	Get #f, 12, n
	s = Space(4)
	For i As UByte = 1 To n
		Get #f, , s
		Select Case s
			Case "Base" : Get #f, , fp(1)
			Case "Pars" : Get #f, , fp(2)
			Case "Shps" : Get #f, , fp(3)
			Case "Dash" : Get #f, , fp(4)
			Case Else : Seek #f, Seek(f) + 4
		End Select
	Next i
	For i As UByte = 1 To 4
		fp(i) += 1
	Next i
	
	'Load base
	Get #f, fp(1), nn
	carname = Space(nn)
	Get #f, , carname
	Get #f, , nn
	carsignname = Space(nn)
	Get #f, , carsignname
	Get #f, , nn
	carinfo = Space(nn)
	Get #f, , carinfo
	
	'Load parameters
	Get #f, fp(2), par
	
	'Load shapes
	Get #f, fp(3), paintjobs
	
	For i As Byte = 0 To 4
		Get #f, , car(i).vs
		For j As Short = 0 To car(i).vs - 1
			Get #f, , car(i).v(j).x
			Get #f, , car(i).v(j).y
			Get #f, , car(i).v(j).z
			car(i).v(j).sel = 0
		Next j
		Get #f, , car(i).ps
		For j As Short = 0 To car(i).ps - 1
			Get #f, , car(i).p(j).kind
			Get #f, , car(i).p(j).fcull
			Get #f, , car(i).p(j).bcull
			Get #f, , car(i).p(j).flags
			For k As Short = 0 To paintjobs - 1
				Get #f, , car(i).p(j).m(k)
			Next k
			
			Select Case car(i).p(j).kind
				Case 0 To 10 : vps = car(i).p(j).kind
				Case 11 : vps = 2
				Case 12 : vps = 6
				Case Else : vps = 0
			End Select
			
			For k As Short = 0 To vps - 1
				Get #f, , car(i).p(j).vp(k)
			Next k
		Next j
	Next i
	
	For i As Byte = 0 To 3
		Get #f, , cexp(i).vs
		For j As Short = 0 To cexp(i).vs - 1
			Get #f, , cexp(i).v(j).x
			Get #f, , cexp(i).v(j).y
			Get #f, , cexp(i).v(j).z
			cexp(i).v(j).sel = 0
		Next j
		Get #f, , cexp(i).ps
		For j As Short = 0 To cexp(i).ps - 1
			Get #f, , cexp(i).p(j).kind
			Get #f, , cexp(i).p(j).fcull
			Get #f, , cexp(i).p(j).bcull
			Get #f, , cexp(i).p(j).flags
			For k As Short = 0 To paintjobs - 1
				Get #f, , cexp(i).p(j).m(k)
			Next k
			
			Select Case cexp(i).p(j).kind
				Case 0 To 10 : vps = cexp(i).p(j).kind
				Case 11 : vps = 2
				Case 12 : vps = 6
				Case Else : vps = 0
			End Select
			
			For k As Short = 0 To vps - 1
				Get #f, , cexp(i).p(j).vp(k)
			Next k
		Next j
	Next i
	
	'Load dash
	Get #f, fp(4), bitmaps
	For i As Byte = 1 To bitmaps
		bitmap(i).id = Space(4)
		Get #f, , bitmap(i).id
		Get #f, , bitmap(i).w
		Get #f, , bitmap(i).h
		Get #f, , bitmap(i).uw(1)
		Get #f, , bitmap(i).uw(2)
		Get #f, , bitmap(i).x
		Get #f, , bitmap(i).y
		Get #f, , bitmap(i).rx
		Get #f, , bitmap(i).ry
		Get #f, , bitmap(i).ub(1)
		Get #f, , bitmap(i).ub(2)
		Get #f, , bitmap(i).ub(3)
		Get #f, , bitmap(i).ub(4)
		Get #f, , bitmap(i).ismask
		Get #f, , bitmap(i).hasmask
		bitmap(i).maskname = Space(4)
		Get #f, , bitmap(i).maskname
		
		'Load image itself
		bitmap(i).i = Allocate(bitmap(i).w * bitmap(i).h)
		Get #f, , *bitmap(i).i, bitmap(i).w * bitmap(i).h
		bitmap(i).i2 = ImageCreate(2 * bitmap(i).w, 2 * bitmap(i).h)
		bitmap(i).i3 = ImageCreate(3 * bitmap(i).w, 3 * bitmap(i).h)
		
		If bitmap(i).ismask Then
			d = 0
			For y As Short = 0 To bitmap(i).h - 1
				For x As Short = 0 To bitmap(i).w - 1
					Line bitmap(i).i2, (2 * x, 2 * y)- Step (1, 1), IIf(bitmap(i).i[d], &HFFFFFFFF, 0), B
					Line bitmap(i).i3, (3 * x, 3 * y)- Step (2, 2), IIf(bitmap(i).i[d], &HFFFFFFFF, 0), BF
					d += 1
				Next x
			Next y
		Else
			d = 0
			For y As Short = 0 To bitmap(i).h - 1
				For x As Short = 0 To bitmap(i).w - 1
					Line bitmap(i).i2, (2 * x, 2 * y)- Step (1, 1), pal(bitmap(i).i[d]).c, BF
					Line bitmap(i).i3, (3 * x, 3 * y)- Step (2, 2), pal(bitmap(i).i[d]).c, BF
					d += 1
				Next x
			Next y
		End If
	Next i
	
	Close f
End Sub


Sub SaveCar (finame As String)
	Dim f As Integer
	Dim As Long pbase, pshape, pdash, pparams
	
	f = FreeFile
	File_File = finame
	Open finame For Output As f
	If Err Then
		File_ErNum = 2
		File_Error = "Invalid file name"
		Exit Sub
	Else
		File_ErNum = 0
		Close f
	End If
	Open finame For Binary Access Write As f
	
	'Save header -----------------------------------
	Put #f, , "*car" & Chr(13, 10, 26, 0) 'Magic number
	Put #1, , Chr(1, 0, 0)	'Format version
	
	'Save index
	Put #f, , Chr(4) & "Base" & MkL(0) & "Pars" & MkL(0) & "Shps" & MkL(0) & "Dash" & MkL(0)
	
	'Save base ----------------------------
	pbase = Seek(f) - 1
	Put #f, , MkShort(Len(carname)) & carname
	Put #f, , MkShort(Len(carsignname)) & carsignname
	Put #f, , MkShort(Len(carinfo)) & carinfo
	Put #f, , Chr(0)
	
	'Save parameters ----------------------
	pparams = Seek(f) - 1
	Put #f, , par
	
	'Save shapes --------------------------
	pshape = Seek(f) - 1
	Dim vps As UByte
	
	Put #f, , paintjobs		'Save number of paint jobs
	For i As Byte = 0 To 4		'Save all 5 cars shapes
		Put #f, , car(i).vs		'Number of vertices
		For j As Short = 0 To car(i).vs - 1
			Put #f, , car(i).v(j).x
			Put #f, , car(i).v(j).y
			Put #f, , car(i).v(j).z
		Next j
		Put #f, , car(i).ps		'Number of primitives
		For j As Short = 0 To car(i).ps - 1
			Put #f, , car(i).p(j).kind
			Put #f, , car(i).p(j).fcull
			Put #f, , car(i).p(j).bcull
			Put #f, , car(i).p(j).flags
			For k As Short = 0 To paintjobs - 1
				Put #f, , car(i).p(j).m(k)
			Next k
			
			Select Case car(i).p(j).kind
				Case 0 To 10 : vps = car(i).p(j).kind
				Case 11 : vps = 2
				Case 12 : vps = 6
				Case Else : vps = 0
			End Select
			
			For k As Short = 0 To vps - 1
				Put #f, , car(i).p(j).vp(k)
			Next k
		Next j
	Next i
	
	For i As Byte = 0 To 3		'Save all 4 debris shapes
		Put #f, , cexp(i).vs	'Number of vertices
		For j As Short = 0 To cexp(i).vs - 1
			Put #f, , cexp(i).v(j).x
			Put #f, , cexp(i).v(j).y
			Put #f, , cexp(i).v(j).z
		Next j
		Put #f, , cexp(i).ps	'Number of primitives
		For j As Short = 0 To cexp(i).ps - 1
			Put #f, , cexp(i).p(j).kind
			Put #f, , cexp(i).p(j).fcull
			Put #f, , cexp(i).p(j).bcull
			Put #f, , cexp(i).p(j).flags
			For k As Short = 0 To paintjobs - 1
				Put #f, , cexp(i).p(j).m(k)
			Next k
			
			Select Case cexp(i).p(j).kind
				Case 0 To 10 : vps = cexp(i).p(j).kind
				Case 11 : vps = 2
				Case 12 : vps = 6
				Case Else : vps = 0
			End Select
			
			For k As Short = 0 To vps - 1
				Put #f, , cexp(i).p(j).vp(k)
			Next k
		Next j
	Next i
	
	'Save dash ----------------------------
	pdash = Seek(f) - 1
	
	Put #f, , bitmaps
	For i As Byte = 1 To bitmaps
		Put #f, , bitmap(i).id & Space(4 - Len(bitmap(i).id))
		Put #f, , bitmap(i).w
		Put #f, , bitmap(i).h
		Put #f, , bitmap(i).uw(1)
		Put #f, , bitmap(i).uw(2)
		Put #f, , bitmap(i).x
		Put #f, , bitmap(i).y
		Put #f, , bitmap(i).rx
		Put #f, , bitmap(i).ry
		Put #f, , bitmap(i).ub(1)
		Put #f, , bitmap(i).ub(2)
		Put #f, , bitmap(i).ub(3)
		Put #f, , bitmap(i).ub(4)
		Put #f, , bitmap(i).ismask
		Put #f, , bitmap(i).hasmask
		Put #f, , bitmap(i).maskname & Space(4 - Len(bitmap(i).maskname))
	
		'Save image itself
		Put #f, , *bitmap(i).i, bitmap(i).w * bitmap(i).h
	Next i
	
	'Store pointers
	Put #f, 17, pbase
	Put #f, 25, pparams
	Put #f, 33, pshape
	Put #f, 41, pdash
	
	Close f
End Sub


Sub DrawSection (x1 As Short, y1 As Short, x2 As Short, _
				y2 As Short, title As String = "", highlight As Byte = 0)

	Dim c As ULong
	
	Line (x1, y1)-(x2, y2), BRIGHT_COL, B
	Line (x1 - 1, y1 - 1)-(x2 - 1, y2 - 1), DARK_COL, B
	
	If Len(title) Then
		Line (x1 + 16, y1 - 2)- Step (8 * (Len(title) + 2), 4), B_COL, BF
		If highlight Then c = RGB(250, 250, 250) Else c = TEXT_COL
		Draw String (x1 + 24, y1 - 8), title, c
	End If
End Sub


Sub UpdatePG (reverse As Byte = 0)
	'Update powerger indicator to match mass
	'or mass to match powergear indicator
	
	Dim diff As Double, prevmass As Long = -60000, nextmass As Long = 65535
	Dim pg As Byte
	
	If reverse Then
		For i As Long = par.mass To 65535
			diff = Frac(65536 / i)
			Select Case diff
				Case 0 : pg = 4
				Case 0 To 1 / 6 : pg = 0
				Case 1 / 6 To 1 / 2 : pg = 1
				Case 1 / 2 To 5 / 6 : pg = 3
				Case Else : pg = 2
			End Select
			If pg = ptarget(2).value Then
				nextmass = i
				Exit For
			End If
		Next i
		
		For i As Long = par.mass To 0 Step -1
			diff = Frac(65536 / i)
			Select Case diff
				Case 0 : pg = 4
				Case 0 To 1 / 6 : pg = 0
				Case 1 / 6 To 1 / 2 : pg = 1
				Case 1 / 2 To 5 / 6 : pg = 3
				Case Else : pg = 2
			End Select
			If pg = ptarget(2).value Then
				prevmass = i
				Exit For
			End If
		Next i
		
		If prevmass >= 0 AndAlso Abs(par.mass - prevmass) < Abs(par.mass - nextmass) Then
			par.mass = prevmass
		Else
			par.mass = nextmass
		End If
	Else
		diff = Frac(65536 / par.mass)
		Select Case diff
			Case 0 : ptarget(2).value = 4
			Case 0 To 1 / 6 : ptarget(2).value = 0
			Case 1 / 6 To 1 / 2 : ptarget(2).value = 1
			Case 1 / 2 To 5 / 6 : ptarget(2).value = 3
			Case Else : ptarget(2).value = 2
		End Select
	End If
End Sub


Sub EditTarget (n As Short)
	Dim maxlength As Short, istext As Byte, s As String
	Dim akey As String, update As Byte = -1, curpos As Short
	Dim isnum As Byte
	Dim As Integer xm, ym, bm, exbm
	
	If ptarget(n).kind >= 3 Then
		istext = -1 : isnum = 0
		maxlength = ptarget(n).max
		Select Case ptarget(n).kind
			Case 3 : s = carname
			Case 4 : s = carsignname
		End Select
	ElseIf ptarget(n).kind >= 1 Then
		istext = 0 : isnum = -1
		maxlength = 5
		Select Case ptarget(n).kind
			Case 1 : s = Str(par.b(ptarget(n).ref))
			Case 2 : s = Str(par.w(ptarget(n).ref \ 2))
		End Select
	ElseIf ptarget(n).kind = 0 Then
		Dim As Integer xm, ym, bm
		ptarget(n).value = (ptarget(n).value + 1) Mod (ptarget(n).max + 1)
		
		maxlength = 20
		Line (ptarget(n).xvalue - 8, ptarget(n).yvalue - 1)- Step (8 * (maxlength + 2), 16), B_COL, BF
		Do
			GetMouse xm, ym, , bm
		Loop Until bm <> 1
		
		UpdatePG -1
		Exit Sub
	End If
	
	GetMouse xm, ym, , exbm
	curpos = Len(s)
	
	Do
		If update Then
			ScreenLock
			Line (ptarget(n).xvalue - 8, ptarget(n).yvalue - 1)- Step (8 * (maxlength + 2), 16), SEL_COL, BF
			Draw String (ptarget(n).xvalue, ptarget(n).yvalue), s, RGB(100, 255, 100)
			Line (ptarget(n).xvalue + 8 * curpos, ptarget(n).yvalue)- Step (0, 15), RGB(180, 255, 180)
			ScreenUnlock
			update = 0
		End If
		
		GetMouse xm, ym, , bm
		
		If bm <> exbm Then
			exbm = bm
			If bm <> 0 Then Exit Do
		End If
		
		akey = InKey
		Select Case akey
			Case Chr(255, 77)
				If curpos < Len(s) Then
					curpos += 1
					update = -1
				End If
			Case Chr(255, 75)
				If curpos > 0 Then
					curpos -= 1
					update = -1
				End If
			Case Chr(8)
				If curpos > 0 Then
					s = Left(s, curpos - 1) + Mid(s, curpos + 1)
					If isnum Then
						If ValInt(s) < ptarget(n).min Or Len(s) = 0 Then
							s = Str(ptarget(n).min)
							curpos = Len(s)
						Else
							curpos -= 1
						End If
					Else
						curpos -= 1
					End If
					update = -1
				End If
			Case "0" To "9"
				If Len(s) < maxlength Then
					If isnum Then
						Dim temp As String
						temp = Left(s, curpos) + akey + Mid(s, curpos + 1)
						If ValInt(temp) <= ptarget(n).max Then
							s = temp
							curpos += 1
							If Left(s, 1) = "0" Then
								s = Mid(s, 2)
								curpos -= 1
							End If
							update = -1
						End If
					ElseIf istext Then
						s = Left(s, curpos) + akey + Mid(s, curpos + 1)
						curpos += 1
						update = -1
					End If
				End If
			Case " " To Chr(126)
				If istext <> 0 AndAlso Len(s) < maxlength Then
					s = Left(s, curpos) + akey + Mid(s, curpos + 1)
					curpos += 1
					update = -1
				End If
			Case Chr(255, 83)
				If curpos <> Len(s) Then
					s = Left(s, curpos) + Mid(s, curpos + 2)
					If isnum Then
						If Len(s) = 0 OrElse ValInt(s) < ptarget(n).min Then
							s = Str(ptarget(n).min)
						End If
					End If
					update = -1
				End If
			Case Chr(13)
				Select Case ptarget(n).kind
					Case 1 : par.b(ptarget(n).ref) = ValInt(s)
					Case 2 : par.w(ptarget(n).ref \ 2) = ValInt(s)
					Case 3 : carname = s
					Case 4 : carsignname = s
				End Select
				If ptarget(n).title = "Mass" Then UpdatePG
				Exit Do
			Case Chr(27) : Exit Do
		End Select
	Loop
	
	Line (ptarget(n).xvalue - 8, ptarget(n).yvalue - 1)- Step (8 * (maxlength + 2), 16), B_COL, BF
End Sub


Function NumInput(ByRef n As InputValue) As Byte
	Dim update As Byte = -1, akey As String, t As String
	Dim As Integer xm, ym, wm, bm
	
	If n.init = 0 Then
		If n.fcol = 0 Then n.fcol = NUM_COL
		If n.bcol = 0 Then n.bcol = DARK_COL
		If n.maxlength = 0 Then n.maxlength = 8
		n.s = Left(Str(n.v), 8)
		If Right(n.s, 1) = "." Then n.s = Left(n.s, Len(n.s) - 1)
		n.cursor = Len(n.s)
		n.init = -1
	End If
	
	Do
		GetMouse xm, ym, wm, bm
	Loop Until bm = 0
	
	Do
		If update Then
			ScreenLock
			Line (n.x, n.y)- Step (8 * (n.maxlength + 2) - 1, 15), n.bcol, BF
			Draw String (n.x + 8, n.y), n.s, n.fcol
			Line (n.x + 8 * (n.cursor + 1), n.y + 1) - Step (0, 14), n.fcol
			ScreenUnlock
			update = 0
		End If
		
		GetMouse xm, ym, wm, bm
		If bm Then Return -1	'Interrupted by the mouse. Not done editing yet!
		
		akey = InKey
		Select Case akey
			Case "0" To "9"
				If Len(n.s) < n.maxlength Then
					t = Left(n.s, n.cursor) + akey + Mid(n.s, n.cursor + 1)
					If Val(t) >= n.min AndAlso Val(t) <= n.max Then
						n.cursor += 1
						n.s = t
						update = -1
					End If
				End If
			Case Chr(8)
				If n.cursor > 0 Then
					t = Left(n.s, n.cursor - 1) + Mid(n.s, n.cursor + 1)
					If Val(t) >= n.min AndAlso Val(t) <= n.max Then
						n.s = t
						n.cursor -= 1
						update = -1
					End If
				End If
			Case Chr(255, 83)
				If n.cursor < Len(n.s) Then
					t = Left(n.s, n.cursor) + Mid(n.s, n.cursor + 2)
					If Val(t) >= n.min AndAlso Val(t) <= n.max Then
						n.s = t
						update = -1
					End If
				End If
			Case "-"
				If -Val(n.s) >= n.min AndAlso -Val(n.s) <= n.max Then
					t = Trim(Str(-Val(n.s)))
					If Len(t) <= n.maxlength Then
						n.cursor += Len(t) - Len(n.s)
						n.s = t
						update = -1
					End If
				End If
			Case "."
				If n.fpt Then
					If InStr(n.s, ".") = 0 Then
						t = Left(n.s, n.cursor) + "." + Mid(n.s, n.cursor + 1)
						If Val(t) >= n.min AndAlso Val(t) <= n.max AndAlso Len(t) <= n.maxlength Then
							n.s = t
							n.cursor += 1
							update = -1
						End If
					End If
				End If
			Case Chr(255, 75)
				If n.cursor > 0 Then
					n.cursor -= 1
					update = -1
				End If
			Case Chr(255, 77)
				If n.cursor < Len(n.s) Then
					n.cursor += 1
					update = -1
				End If
			Case Chr(255, 71)
				n.cursor = 0
				update = -1
			Case Chr(255, 79)
				n.cursor = Len(n.s)
				update = -1
			Case Chr(27)
				Exit Do	'No modification to the value. Edition ended
			Case Chr(13)
				n.v = Val(n.s)
				Exit Do	'Edition ended with value updated
			Case " " To Chr(126)
				If InStr(n.allowed, akey) AndAlso Len(n.s) < n.maxlength Then
					t = Left(n.s, n.cursor) + akey + Mid(n.s, n.cursor + 1)
					If Val(t) >= n.min AndAlso Val(t) <= n.max Then
						n.cursor += 1
						n.s = t
						update = -1
					End If
				End If
		End Select
	Loop
	
	ScreenLock
	Line (n.x, n.y)- Step (8 * (n.maxlength + 2) - 1, 15), n.bcol, BF
	Draw String (n.x + 8, n.y), Trim(Str(n.v)), n.fcol
	ScreenUnlock
	
	Return 0
End Function


Function MaintainTargets(from As Short, till As Short) As Short
	Static As Integer exxm, exym, exbm, exwm
	Dim As Integer xm, ym, bm, wm
	Dim s As String, toedit As Short = -1
	Dim field_width As Short
	
	GetMouse xm, ym, wm, bm
	If xm = exxm AndAlso ym = exym AndAlso bm = exbm _
		AndAlso wm = exwm Then Return 0
	
	ScreenLock
	For i As Short = from To till
		If ptarget(i).ref >= 0 Then
			Select Case ptarget(i).kind
				Case 1 : s = Str(par.b(ptarget(i).ref))
				Case 2 : s = Str(par.w(ptarget(i).ref \ 2))
				Case 3 : s = Left(carname, 21)
				Case 4 : s = Left(carsignname, 21)
				Case Else : s = "?"
			End Select
		Else
			s = textoption(ptarget(i).opt + ptarget(i).min + ptarget(i).value)
		End If
		
		Select Case ptarget(i).kind
			Case 0 : field_width = 20
			Case 1, 2 : field_width = 5
			Case 3, 4 : field_width = ptarget(i).max
			Case Else : field_width = 0
		End Select
		
		If xm >= ptarget(i).xtitle AndAlso xm < ptarget(i).xtitle + 8 * ptarget(i).twidth _
			AndAlso ym >= ptarget(i).ytitle AndAlso ym < ptarget(i).ytitle + 16 Then
		
			Draw String (ptarget(i).xtitle, ptarget(i).ytitle), ptarget(i).title, WHITE_COL
			Line (ptarget(i).xvalue, ptarget(i).yvalue)- Step(8 * field_width - 1, 15), B_COL, BF
			Draw String (ptarget(i).xvalue, ptarget(i).yvalue), s, RGB(0, 250, 0)
		
			If bm = 1 Then		'Clicked on field
				toedit = i
			End If
		Else
			Draw String (ptarget(i).xtitle, ptarget(i).ytitle), ptarget(i).title, GRAY_COL
			Line (ptarget(i).xvalue, ptarget(i).yvalue)- Step(8 * field_width - 1, 15), B_COL, BF
			Draw String (ptarget(i).xvalue, ptarget(i).yvalue), s, RGB(0, 180, 100)
		End If
	Next i
	ScreenUnlock
	
	exxm = xm : exym = ym : exbm = bm : exwm = wm
	
	If toedit > -1 Then
		EditTarget toedit
		Return 1
	Else
		Return 0
	End If
End Function


Function MaintainMenu (drawmenu As Byte = 0) As Short
	Static As Integer exxm, exym, exbm
	Dim As Integer xm, ym, bm
	Dim c As ULong, changedmenu As Byte
	
	GetMouse xm, ym, , bm
	
	If drawmenu = 0 Then
		'Only continue if something changed
		If exxm = xm AndAlso exym = ym AndAlso exbm = bm Then Return 0
				
		'Do nothing if outside the menu bar
		If ym < 600 And exym < 600 Then Return 0
	
		exxm = xm : exym = ym : exbm = bm
	End If
	
	ScreenLock
	For i As Short = 0 To 4
		If i = curmenu Then
			c = SEL_COL
		ElseIf ym >= 600 AndAlso xm >= 48 * i AndAlso xm < 48 * (i + 1) Then
			If bm = 1 Then
				c = SEL_COL
				curmenu = i
				changedmenu = -1
			Else
				c = H_COL
			End If
		Else
			c= B_COL
		End If
		Line (48 * i, 600)- Step (47, 47), c, BF
	Next i
	Put (0, 600), menuimg, Alpha
	ScreenUnlock
	
	If changedmenu Then
		Return -1
	ElseIf bm <> 1 Then
		Return 0
	Else
		Return xm
	End If
End Function


Sub EditSpecs (onlydisplay As Byte = 0)
	Dim specline(1 To 9) As String, speclines As Byte
	Dim n As Short, buffer As String
	Dim As Integer xm, ym, bm, exbm
	
	GetMouse xm, ym, , exbm
	
	buffer = carinfo
	Do
		n = InStr(buffer, "]")
		If n Then
			speclines += 1
			specline(speclines) = Left(buffer, n - 1)
			specline(speclines) = Left(specline(speclines), 23)
			buffer = Mid(buffer, n + 1)
		ElseIf Len(buffer) Then
			speclines += 1
			specline(speclines) = Left(buffer, 23)
		End If			
	Loop Until speclines = 9 OrElse Len(buffer) = 0

	If onlydisplay = 0 Then
		Dim update As Byte = -1, curx As Short, cury As Short
		Dim akey As String
		
		Do
			If update Then
				ScreenLock
				Line (745, 355)-(943, 565), SEL_COL, BF
				For i As Byte = 1 To speclines
					Draw String (750, 365 + 16 * (i - 1)), specline(i), RGB(0, 250, 0)
				Next i
				Line (750 + curx * 8, 365 + 16 * cury)- Step (0, 15), RGB(100, 250, 100)
				ScreenUnlock
				update = 0
			End If
		
			akey = InKey
			GetMouse xm, ym, , bm
			
			If bm <> exbm Then
				exbm = bm
				If bm <> 0 Then Exit Do
			End If
			
			Select Case akey
				Case Chr(255, 77)
					If curx < Len(specline(cury + 1)) Then
						curx += 1
						update = -1
					End If
				Case Chr(255, 75)
					If curx > 0 Then
						curx -= 1
						update = -1
					End If
				Case Chr(255, 72)
					If cury > 0 Then
						If cury + 1 = speclines AndAlso Len(specline(cury + 1)) = 0 Then speclines -= 1
						cury -= 1
						If curx > Len(specline(cury + 1)) Then
							curx = Len(specline(cury + 1))
						End If
						update = -1
					End If
				Case Chr(255, 80)
					If cury < 8 Then
						cury += 1
						If curx > Len(specline(cury + 1)) Then
							curx = Len(specline(cury + 1))
						End If
						If cury + 1 > speclines Then speclines = cury + 1
						update = -1
					End If
				Case " " To Chr(126)
					specline(cury + 1) = Left(specline(cury + 1), curx) & akey & Mid(specline(cury + 1), curx + 1)
					specline(cury + 1) = Left(specline(cury + 1), 23)
					curx += 1
					update = -1
				Case Chr(8)
					If curx >= 1 Then
						specline(cury + 1) = Left(specline(cury + 1), curx - 1) & Mid(specline(cury + 1), curx + 1)
						curx -= 1
						update = -1
					End If
				Case Chr(255, 71)
					curx = 0
					update = -1
				Case Chr(255, 79)
					curx = Len(specline(cury + 1))
					update = -1
				Case Chr(255, 83)
					If curx < Len(specline(cury + 1)) Then
						specline(cury + 1) = Left(specline(cury + 1), curx) & Mid(specline(cury + 1), curx + 2)
						update = -1
					End If
				Case Chr(9)
					If cury < 8 Then
						For i As Byte = 1 To 9
							specline(i) = ""
						Next i
						curx = 0 : cury = 0
						update = -1
					End If
				Case Chr(13)
					carinfo = ""
					speclines = 9
					For i As Byte = 9 To 1 Step -1
						If Len(specline(i)) Then
							Exit For
						Else
							speclines -= 1
						End If
					Next i
					For i As Byte = 1 To speclines
						carinfo &= RTrim(specline(i)) & "]"
					Next i
					Exit Do
				Case Chr(27) : Exit Do
			End Select
		Loop
	End If
	
	
	speclines = 0
	buffer = carinfo
	Do
		n = InStr(buffer, "]")
		If n Then
			speclines += 1
			specline(speclines) = Left(buffer, n - 1)
			specline(speclines) = Left(specline(speclines), 23)
			buffer = Mid(buffer, n + 1)
		ElseIf Len(buffer) Then
			speclines += 1
			specline(speclines) = Left(buffer, 23)
		End If			
	Loop Until speclines = 9 OrElse Len(buffer) = 0
	
	ScreenLock
	Line (745, 355)-(943, 565), B_COL, BF
	
	For i As Byte = 1 To speclines
		Draw String (750, 365 + 16 * (i - 1)), specline(i), RGB(0, 180, 100)
	Next i
	ScreenUnlock
End Sub


Sub EditParams
	Dim As Integer xm, ym, wm, bm
	Dim As Integer exxm, exym, exbm
	Dim m As Short, update As Byte = -1, myst As Byte = -1
	Dim akey As String, multi As Short = 2, n As Short
	Dim s As String, pRPM As UShort, expRPM As UShort
	Dim holding As Byte = 0, curRPMp As Byte
	
	Line (0, 0)-(959, 599), B_COL, BF
	Draw String (16, 16), "= PHYSICAL PARAMETERS =", WHITE_COL
	DrawSection 10, 50, 300, 195, "General"
	DrawSection 320, 50, 510, 180, "Grip"
	DrawSection 530, 50, 720, 195, "Gears"
	If myst Then
		DrawSection 740, 50, 948, 324, "Mysterious"
	Else
		DrawSection 740, 50, 948, 144, "RPM"
	End If
	DrawSection 10, 220, 720, 570, "Torque curve"
	DrawSection 740, 345, 948, 570, "Specifications"
	
	EditSpecs -1
	
	Do
		If update Then
			ScreenLock
			Line (12, 230)-(718, 564), B_COL, BF
			
			'Background for RPM knob number
			Line (72, 545)- Step (159, 15), B_COL, BF
			
			For i As Byte = 1 To 4
				Dim r As UShort, col As ULong
				
				Select Case i
					Case 1
						r = par.idleRPM : col = RGB(180, 160, 0)
						s = "I"
						If holding = 1 Then Draw String (72, 545), "Idle: " & r & " RPM", col
					Case 2
						r = par.dsRPM : col = RGB(60, 60, 180)
						s = "D"
						If holding = 2 Then Draw String (72, 545), "Downshift: " & r & " RPM", col
					Case 3
						r = par.usRPM : col = RGB(60, 180, 180)
						s = "U"
						If holding = 3 Then Draw String (72, 545), "Upshift: " & r & " RPM", col
					Case 4
						r = par.maxRPM : col = RGB(240, 80, 0)
						s = "M"
						If holding = 4 Then Draw String (72, 545), "Max: " & r & " RPM", col
				End Select
				r = 6 * r / 128
				Line (65 + r, 500 - 255)- Step (0, 280), col
				Circle (64 + r, 532), 8, col, , , , F
				Draw String (61 + r, 525), s, RGB(0, 0, 0)
			Next i
			
			Line (55, 500 - 255)-(65, 500 - 255), GRAY_COL
			Line (65, 500 - 255)-(65, 500), GRAY_COL
			Line (55, 500)-(65 + 6 * 103, 500), GRAY_COL
			Draw String (29, 492 - 255), Str((256 \ multi) - 1), GRAY_COL
			Draw String (45, 492), "0", GRAY_COL
			
			For i As LongInt = 0 To 13000 Step 1000
				Line (65 + (6 * i / 128), 500)- Step (0, 7), GRAY_COL
				s = Str(i \ 1000) & "k"
				Draw String (65 + (6 * i / 128) - 8 * (Len(s) / 2), 510), s, GRAY_COL
			Next i
			
			n = par.torque(0) * multi
			If n > 255 Then n = 255
			PSet (65, 500 - n), RGB(0, 180, 0)
			For i As UByte = 0 To 103
				n = par.torque(i) * multi
				If n > 255 Then n = 255
				Line -(65 + 6 * i, 500 - n), RGB(0, 180, 0)
			Next i
			For i As UByte = 0 To 103
				Dim col As ULong
				
				n = par.torque(i) * multi
				If i = curRPMp Then
					col = RGB(0, 250, 250)
				ElseIf n > 255 Then
					n = 255
					col = RGB(255, 0, 250)
				ElseIf par.torque(i) >= 128 Then
					col = RGB(250, 80, 0)
				Else
					col = RGB(0, 200, 0)
				End If
				
				Line (64 + 6 * i, 499 - n)- Step (2, 2), col, B
			Next i
			ScreenUnlock
			update = 0
		End If
		
		If (exxm <> xm OrElse exym <> ym OrElse exbm <> bm) Then
			If xm >= 65 AndAlso xm <= 686 AndAlso ym >= 245 AndAlso ym <= 500 Then
		
				pRPM = 128 * ((xm - 65) \ 6)
				If pRPM <> expRPM Then
					Dim col As ULong
					
					n = par.torque(expRPM \ 128) * multi
					If pRPM \ 128 = curRPMp Then
						col = RGB(0, 250, 250)
					ElseIf n > 255 Then
						n = 255
						col = RGB(255, 0, 250)
					ElseIf par.torque(expRPM \ 128) >= 128 Then
						col = RGB(250, 80, 0)
					Else
						col = RGB(0, 200, 0)
					End If
					
					ScreenLock
					If n < 256 Then _
						Line (64 + 6 * expRPM \ 128, 499 - n)- Step (2, 2), col, B
					n = par.torque(pRPM \ 128) * multi
					If n < 256 Then _
						Line (64 + 6 * pRPM \ 128, 499 - n)- Step (2, 2), WHITE_COL, B
					
					Line (520, 545)- Step (169, 15), B_COL, BF
					Draw String (520, 545), pRPM & " RPM", GRAY_COL
					Draw String (610, 545), "Torque=" & par.torque(pRPM \ 128), GRAY_COL
					ScreenUnlock
					expRPM = pRPM
				End If
				
				'~ If bm = 1 Then
					'~ par.torque(pRPM \ 128) = (500 - ym) \ multi
					'~ curRPMp = pRPM \ 128
					'~ update = -1
				'~ ElseIf bm = 2 Then
					'~ curRPMp = pRPM \ 128
					'~ update = -1
				'~ End If
				
				If bm = 1 Then
					par.torque(pRPM \ 128) = (500 - ym) \ multi
					update = -1
				End If
				curRPMp = pRPM \ 128
			ElseIf xm >= 65 AndAlso xm <= 686 AndAlso ym >= 510 AndAlso ym <= 540 Then
				Dim r As Long, dist As Long = 1000
				
				r = (128 * (xm - 65)) \ 6
				If bm = 1 AndAlso exbm <> 1 Then	'Grab a hold
					'See which RPM tab is nearest
					If Abs(par.idleRPM - r) < dist Then holding = 1 : dist = Abs(par.idleRPM - r)
					If Abs(par.dsRPM - r) < dist Then holding = 2 : dist = Abs(par.dsRPM - r)
					If Abs(par.usRPM - r) < dist Then holding = 3 : dist = Abs(par.usRPM - r)
					If Abs(par.maxRPM - r) < dist Then holding = 4 : dist = Abs(par.maxRPM - r)
					'If dist > 10 Then holding = 0
				ElseIf bm <> 1 Then
					holding = 0
				End If
				
				Select Case holding
					Case 1 : par.idleRPM = r
					Case 2 : par.dsRPM = r
					Case 3 : par.usRPM = r
					Case 4 : par.maxRPM = r
				End Select
				If holding Then update = -1
			ElseIf xm >= 750 AndAlso ym >= 355 AndAlso xm < 938 AndAlso ym < 560 Then
				If bm = 1 Then EditSpecs 0
			End If
								
			exxm = xm :	exym = ym : exbm = bm
		End If
		
		GetMouse xm, ym, wm, bm
		akey = InKey
		m = MaintainMenu
		If m < 0 Then Exit Do
		
		If myst Then
			MaintainTargets(1, 35)
		Else	'This doesn't work well because MaintainTargets uses static variables
			MaintainTargets(1, 18)
			MaintainTargets(34, 39)
		End If
		
		Select Case akey
			Case "i", "I"	'Change idle RPM
				If xm >= 65 AndAlso xm <= 686 AndAlso ym >= 245 AndAlso ym <= 500 Then
					par.idleRPM = 128 * (xm - 65) / 6
					update = -1
				End If
			Case "d", "D"	'Change downshift RPM
				If xm >= 65 AndAlso xm <= 686 AndAlso ym >= 245 AndAlso ym <= 500 Then
					par.dsRPM = 128 * (xm - 65) / 6
					update = -1
				End If
			Case "u", "U"	'Change upshift RPM
				If xm >= 65 AndAlso xm <= 686 AndAlso ym >= 245 AndAlso ym <= 500 Then
					par.usRPM = 128 * (xm - 65) / 6
					update = -1
				End If
			Case "m", "M"	'Change max RPM
				If xm >= 65 AndAlso xm <= 686 AndAlso ym >= 245 AndAlso ym <= 500 Then
					par.maxRPM = 128 * (xm - 65) / 6
					update = -1
				End If
			Case "z", "Z"
				multi += 1
				If multi >= 5 Then multi = 1
				update = -1
			Case Chr(255, 77)
				If curRPMp < 103 Then curRPMp += 1 : update = -1
			Case Chr(255, 75)
				If curRPMp > 0 Then curRPMp -= 1 : update = -1
			Case Chr(255, 72)
				If par.torque(curRPMp) < 255 Then
					par.torque(curRPMp) += 1
					update = -1
				End If
			Case Chr(255, 80)
				If par.torque(curRPMp) > 0 Then
					par.torque(curRPMp) -= 1
					update = -1
				End If
			Case Chr(13), " "
				Dim tq As InputValue
				
				Draw String (40, 543), "New torque:", WHITE_COL
				tq.v = par.torque(curRPMp)
				tq.min = 0 : tq.max = 255 : tq.maxlength = 4
				tq.x = 140 : tq.y = 543
				NumInput tq
				par.torque(curRPMp) = tq.v
				update = -1
			Case Chr(27), Chr(255) & "k" : curmenu = -1 : Exit Sub
		End Select
	Loop
	Line (0, 0)-(959, 599), B_COL, BF
End Sub


Sub ViewManual
	Dim akey As String, m As Short, update As Byte = -1
	Dim As Integer xm, ym, wm, bm, exwm
	Dim titleimg As Any Ptr
	
	Dim mline(1 To 1000) As String, mlines As Short
	
	titleimg = LoadRGBAfile(progdir & "cwtitle.png")
	
	If FileExists(progdir & "manual.txt") Then
		Dim f As Integer
		
		f = FreeFile
		Open progdir & "manual.txt" For Input As f
		While Not EoF(f)
			mlines += 1
			Line Input #f, mline(mlines)
			If mlines = UBound(mline) Then Exit While
		WEnd
		Close f
		
		If titleimg Then
			For i As Byte = 1 To 7
				mline(i) = ""
			Next i
		End If
	Else
		mline(1) = "No manual found"
		mlines = 1
	End If

	Line (0, 0)-(959, 599), B_COL, BF
	Draw String (16, 16), "= PROGRAM MANUAL =", WHITE_COL
	
	Do
		GetMouse xm, ym, wm, bm
	Loop Until bm = 0
	
	GetMouse xm, ym, exwm, bm
	
	Do
		If update Then
			ScreenLock
			View Screen (0, 32)-(959, 599)
			Line (0, 32)-(959, 599), B_COL, BF
			For i As Short = manual_line To manual_line + 32
				If i > mlines Then Exit For
				If Left(LTrim(mline(i)), 3) = "--=" AndAlso Right(RTrim(mline(i)), 3) = "=--" Then
					Draw String (166, 49 + 16 * (i - manual_line)), mline(i), RGB(0, 0, 0)
					Draw String (164, 48 + 16 * (i - manual_line)), mline(i), WHITE_COL
				ElseIf ValInt(mline(i)) <> 0 AndAlso InStr(mline(i), ".") > 0 AndAlso InStr(LTrim(mline(i)), ".") < 5 Then
					Draw String (164, 48 + 16 * (i - manual_line)), mline(i), RGB(0, 250, 100)
				ElseIf InStr(mline(i), "[") = 0 Then
					Draw String (164, 48 + 16 * (i - manual_line)), mline(i), GRAY_COL
				Else
					Dim As Short x, y, ob, cb
					Dim c As ULong, s As String
					
					x = 164 : y = 48 + 16 * (i - manual_line)
					s = mline(i)
					Do While Len(s)
						ob = InStr(s, "[")
						cb = InStr(s, "]")
						If ob = 0 OrElse cb <= ob Then
							Draw String (x, y), s, GRAY_COL
							Exit Do
						ElseIf ob > 1 Then
							Draw String (x, y), Left(s, ob - 1), GRAY_COL
							s = Mid(s, ob)
							x += 8 * (ob - 1)
						Else
							Draw String (x, y), Left(s, cb), RGB(200, 180, 0)
							s = Mid(s, cb + 1)
							x += 8 * cb
						End If
					Loop
				End If
			Next i
			
			If manual_line <= 7 Then Put (260, 16 * (6 - manual_line)), titleimg, Alpha
			View
			ScreenUnlock
			update = 0
		End If
		
		GetMouse xm, ym, wm, bm
		akey = InKey
		m = MaintainMenu
		If m < 0 Then Exit Do
		
		If wm <> -1 AndAlso exwm <> wm Then
			If exwm > wm Then
				manual_line += exwm - wm
				If manual_line > mlines - 10 Then manual_line = mlines - 10
				update = -1
			Else
				manual_line -= wm - exwm
				If manual_line < 1 Then manual_line = 1
				update = -1
			End If
			exwm = wm
		End If
		
		If bm = 1 Then
			If xm >= 164 AndAlso xm <= 795 AndAlso ym >= 48 AndAlso ym <= 559 Then
				Dim l As Short
				
				l = (ym - 48) \ 16 + manual_line
				If ValInt(mline(l)) > 0 AndAlso ValInt(mline(l)) < 100 AndAlso InStr(mline(l), ".") <> 0 Then
					For i As Short = 1 To mlines
						If Trim(mline(i)) = "." & Str(ValInt(mline(l))) & "." Then
							manual_line = i
							Exit For
						End If
					Next i
					
					If manual_line > mlines - 10 Then manual_line = mlines - 10
					If manual_line < 1 Then manual_line = 1
					update = -1
					
					Do
						GetMouse xm, ym, wm, bm
					Loop Until bm = 0
				End If
			End If
		End If
		
		Select Case akey
			Case Chr(255, 72)
				If manual_line > 1 Then
					manual_line -= 1
					update = -1
				End If
			Case Chr(255, 80)
				If manual_line < mlines - 10 Then
					manual_line += 1
					update = -1
				End If
			Case Chr(255, 71)
				manual_line = 1
				update = -1
			Case Chr(255, 79)
				manual_line = mlines - 32
				If manual_line < 1 Then manual_line =1
				update = -1
			Case Chr(255, 73)
				manual_line -= 30
				If manual_line < 1 Then manual_line = 1
				update = -1
			Case Chr(255, 81)
				manual_line += 30
				If manual_line > mlines - 32 Then manual_line = mlines - 32
				If manual_line < 1 Then manual_line = 1
				update = -1
			Case Chr(27), Chr(255) & "k" : curmenu = -1 : Exit Sub
		End Select
	Loop
	
	Line (0, 0)-(959, 599), B_COL, BF
	ImageDestroy titleimg
End Sub


Sub SaveVSH (finame As String)
	Dim list As String, n As Short, s As String
	Dim f As Integer, ress As Short, curres As Short
	Dim fptr As Long
	
	n = InStrRev(finame, SEP)
	If n Then
		s = Mid(finame, n + 1)
	Else
		s = finame
	End If
	If LCase(Left(s, 4)) = "stda" Then
		list = "/!cg0/!eg0/dash/dast/dasm/gbox/ins1/inm1/ins2/ins3/inm3/roof/whl1/whl2/whl3/"
	Else
		list = "/!cg0/!eg0/dot /dot1/dot2/dota/gnab/gnob/"
		For i As Byte = 0 To 9
			list &= "dig" & i & "/"
		Next i
	End If
	
	f = FreeFile
	File_File = finame
	Open finame For Output As f
	If Err Then
		File_ErNum = 2
		File_Error = "Invalid file name"
		Exit Sub
	Else
		File_ErNum = 0
		Close f
	End If
	Open finame For Binary Access Write As f
	
	'Calculate and store number of resources
	ress = 0
	For i As Short = 1 To bitmaps
		If bitmap(i).i <> 0 AndAlso InStr(list, "/" & bitmap(i).id & "/") <> 0 Then
			ress += 1
		End If
	Next i
	Put #f, 5, MkShort(ress)
	
	'Calculate starting data position
	fptr = 8 * ress + 7
	
	'Start storing resources
	curres = 0
	For i As Short = 1 To bitmaps
		If bitmap(i).i <> 0 AndAlso InStr(list, "/" & bitmap(i).id & "/") <> 0 Then
			'Store ID
			Put #f, 7 + 4 * curres, bitmap(i).id
			'Store pointer to data (relative)
			Put #f, 7 + 4 * (ress + curres), MkL(fptr - 8 * ress - 7)
			'Go to data position
			Seek #f, fptr
			
			'Store image
			Put #f, , bitmap(i).w
			Put #f, , bitmap(i).h
			Put #f, , bitmap(i).uw(1)
			Put #f, , bitmap(i).uw(2)
			Put #f, , bitmap(i).x
			Put #f, , bitmap(i).y
			For j As Byte = 1 To 4
				Put #f, , bitmap(i).ub(j)
			Next j
			Put #f, , *bitmap(i).i, bitmap(i).w * bitmap(i).h
			fptr = Seek(f)
			curres += 1
		End If
	Next i
	Put #f, 1, MkL(LoF(f))
	Close f
End Sub


Sub drawtriangle(ByVal x1 As Short, ByVal y1 As Short, _
				ByVal x2 As Short, ByVal y2 As Short, _
				ByVal x3 As Short, ByVal y3 As Short, col As ULong)
	
	'STEPS TO FOLLOW:
	'Step 1: Sort point by Y
	'Step 2: Drop all triangles out of Y bounds
	'Step 3: Classify as top, bottom or mixed
	'Step 4: If mixed, split the triangle and call the sub twice and exit
	'Step 5: Obtain initial Y and X1 and X2 for first line, plus deltas
	'Step 6: Apply deltas till Y is onscreen
	'Step 7: Draw lines till triangle is complete or out of screen

	Dim currenty As Long
	Dim As Long deltaxi, deltaxf, currentxi, currentxf
	
	'Step 1: Sort point by Y
	If y1 > y2 Then Swap y1, y2 : Swap x1, x2
	If y2 > y3 Then Swap y2, y3 : Swap x2, x3
	If y1 > y2 Then Swap y1, y2 : Swap x1, x2
	
	'Step 2: Drop all triangles out of Y bounds
	If y3 < 0 Or y1 >= SHEIGHT Then Exit Sub
	
	'Step 3: Classify as top, bottom or mixed
	If y1 = y2 AndAlso y2 = y3 Then
		'Just a horizontal line
		'... not drawing anything so far
	ElseIf y2 = y3 Then			'Top
		'Step 5: Obtain initial Y and X1 and X2 for first line, plus deltas
		If x2 > x3 Then Swap x2, x3 : Swap y2, y3	'Sort by X
		currenty = y1
		currentxi = x1 ShL 8 : currentxf = x1 ShL 8
		deltaxi = ((x2 - x1) ShL 8) \ (y2 - y1)	'Add some precision
		deltaxf = ((x3 - x1) ShL 8) \ (y3 - y1)
	ElseIf y1 = y2 Then		'Bottom
		'Step 5: Obtain initial Y and X1 and X2 for first line, plus deltas
		If x1 > x2 Then Swap x1, x2 : Swap y1, y2	'Sort by X
		currenty = y1
		currentxi = x1 ShL 8 : currentxf = x2 ShL 8
		deltaxi = ((x3 - x1) ShL 8) \ (y3 - y1)	'Add some precision
		deltaxf = ((x3 - x2) ShL 8) \ (y3 - y2)
	Else					'Mixed
		'Step 4: If mixed, split the triangle and call the sub twice and exit
		Dim otherx As Short
		
		otherx = (y2 - y1) * (x3 - x1) \ (y3 - y1) + x1
		drawtriangle x1, y1, x2, y2, otherx, y2, col
		drawtriangle x2, y2, otherx, y2, x3, y3, col
		Exit Sub
	End If
	
	'Step 6: Apply deltas till Y is onscreen
	If currenty < 0 Then
		currentxi += Abs(currenty) * deltaxi
		currentxf += Abs(currenty) * deltaxf
		currenty = 0
	End If
	
	'Step 7: Draw lines till triangle is complete or out of screen
	Dim As Short tempxi, tempxf
	Do
		'Only draw if onscreen
		If currentxf >= 0 And (currentxi ShR 8) < SWIDTH Then
			'Clip the line
			If currentxi >= 0 Then tempxi = currentxi ShR 8 Else tempxi = 0
			If (currentxf ShR 8) < SWIDTH Then tempxf = currentxf ShR 8 Else tempxf = SWIDTH - 1
			'Draw it
			If tempxi <> 0 Or tempxf <> 0 Then _ 'Don't know why this IF is necessary to avoid a horrible vertical line
				Line (tempxi, currenty)-(tempxf, currenty), col
		End If
		
		'If triangle is done or we go offscreen, then exit
		If currenty >= y3 OrElse currenty >= SHEIGHT - 1 Then Exit Do
	
		'Update coordinates
		currentxi += deltaxi
		currentxf += deltaxf
		currenty += 1
	Loop
	
	'Complete triangle sides
	Line (x1, y1)-(x2, y2), col
	Line -(x3, y3), col
	Line -(x1, y1), col
End Sub


Sub RotateVisualModel (yaw As Double, pitch As Double, perspective As Byte = 0)
	Dim i As Short, v As VisualVertex
	Dim As Double co, si
	
	'Rotate in Z (yaw)
	co = Cos(yaw) : si = Sin(yaw)
	For i = 0 To vcar.vs - 1
		v = vcar.v(i)
		vcar.v(i).x = v.x * co + v.y * si
		vcar.v(i).y = -v.x * si + v.y * co
	Next i
	
	'Rotate in X (pitch)
	co = Cos(pitch) : si = Sin(pitch)
	For i = 0 To vcar.vs - 1
		v = vcar.v(i)
		vcar.v(i).y = v.y * co + v.z * si
		vcar.v(i).z = -v.y * si + v.z * co
	Next i
	
	If perspective Then		'Apply perspective?
		For i = 0 To vcar.vs - 1
			vcar.v(i).x = vcar.v(i).x * 5 / (5 - vcar.v(i).y)
			vcar.v(i).z = vcar.v(i).z * 5 / (5 - vcar.v(i).y)
		Next i
	End If
End Sub


Sub SortVisualPrimitives
	Dim i As Short
	Dim As VisualVertex v(1 To 3)
	Dim dif As Double
	
	'Get rid of inverted triangles
	For i = 0 To vcar.ps - 1
		If vcar.p(i).kind = 3 AndAlso (vcar.p(i).flags And 1) = 0 Then
			v(1) = vcar.v(vcar.p(i).vp(0))
			v(2) = vcar.v(vcar.p(i).vp(1))
			v(3) = vcar.v(vcar.p(i).vp(2))
			dif = v(1).x * v(2).z + v(2).x * v(3).z + v(3).x * v(1).z
			dif -= v(2).x * v(1).z + v(3).x * v(2).z + v(1).x * v(3).z
			If dif > 0 Then		'> 0 for chirality
				Swap vcar.p(i), vcar.p(vcar.ps - 1)
				vcar.ps -= 1
				If i >= vcar.ps - 1 Then Exit For
			End If
		End If
	Next i
	
	
	'Compute distances
	Dim As Double dist(0 To vcar.ps - 1), mindist, maxdist
	
	For i = 0 To vcar.ps - 1
		Select Case vcar.p(i).kind
			Case 2
				dist(i) = 0
				For j As Byte = 0 To 1
					dist(i) += vcar.v(vcar.p(i).vp(j)).y
				Next j
				dist(i) *= .5
			Case 3
				mindist = 1000 : maxdist = -1000
				For j As Byte = 0 To 2
					dist(i) = vcar.v(vcar.p(i).vp(j)).y
					If dist(i) < mindist Then mindist = dist(i)
					If dist(i) > maxdist Then maxdist = dist(i)
				Next j
				dist(i) = (mindist + maxdist) / 2
			Case 11
				dist(i) = vcar.v(vcar.p(i).vp(0)).y
			Case Else
				dist(i) = 0
		End Select
	Next i
	
	'Now do sort (PENDING: consider Z-bias!)
	Dim q As Byte
	
	Do
		q = 0
		For i = 0 To vcar.ps - 2
			If dist(i) > dist(i + 1) Then
				Swap dist(i), dist(i + 1)
				Swap vcar.p(i), vcar.p(i + 1)
				q = -1
			End If
		Next i
	Loop Until q = 0
End Sub


Sub Update3DWindow (nw As Byte)
	Dim As Double lx1, ly1, lx2, ly2, r
	Dim As Vertex v, w
	Dim c As ULong
	
	View Screen (w3d(nw).x1 + 2, w3d(nw).y1 + 8)-(w3d(nw).x2 - 2, w3d(nw).y2 - 2)
	Line (w3d(nw).x1 + 2, w3d(nw).y1 + 8)-(w3d(nw).x2 - 2, w3d(nw).y2 - 2), B_COL, BF
	
	Circle (w3d(nw).xc, w3d(nw).yc), 2, RGB(250, 250, 250)
	If nw = 4 Then	'Floating solid view
		Dim As VisualVertex u1, u2, u3
		SimplifyModel curmodel, curpj
		RotateVisualModel curyaw, curpitch, -1
		SortVisualPrimitives
		For i As Short = 0 To vcar.ps - 1
			Select Case vcar.p(i).sel
				Case 0 : c = material(vcar.p(i).m).c
				Case 1 : c = RGB(200, 0, 200)
				Case 2 : c = RGB(250, 250, 250)
				Case Else : c = RGB(250, 200, 250)
			End Select
			
			Select Case vcar.p(i).kind
				Case 2
					u1 = vcar.v(vcar.p(i).vp(0))
					u2 = vcar.v(vcar.p(i).vp(1))
					Line (u1.x * w3d(4).zoom + w3d(4).xc, -u1.z * w3d(4).zoom + w3d(4).yc)- _
						(u2.x * w3d(4).zoom + w3d(4).xc, -u2.z * w3d(4).zoom + w3d(4).yc), _
							c
				Case 3
					u1 = vcar.v(vcar.p(i).vp(0))
					u2 = vcar.v(vcar.p(i).vp(1))
					u3 = vcar.v(vcar.p(i).vp(2))
					drawtriangle u1.x * w3d(4).zoom + w3d(4).xc, -u1.z * w3d(4).zoom + w3d(4).yc, _
							u2.x * w3d(4).zoom + w3d(4).xc, -u2.z * w3d(4).zoom + w3d(4).yc, _
							u3.x * w3d(4).zoom + w3d(4).xc, -u3.z * w3d(4).zoom + w3d(4).yc, _
							c
			End Select
		Next i
	Else			'Wire frame views
		For i As Single = -5 To 5 Step .5
			If i = 0 Then
				Color RGB(120, 120, 120)
			ElseIf Int(i) = i Then
				Color RGB(70, 70, 70)
			Else
				Color RGB(60, 40, 40)
			End If
			
			Line (w3d(nw).xc + i * w3d(nw).zoom, w3d(nw).yc - 5 * w3d(nw).zoom)-_
				(w3d(nw).xc + i * w3d(nw).zoom, w3d(nw).yc + 5 * w3d(nw).zoom)
			Line (w3d(nw).xc - 5 * w3d(nw).zoom, w3d(nw).yc + i * w3d(nw).zoom)-_
				(w3d(nw).xc + 5 * w3d(nw).zoom, w3d(nw).yc + i * w3d(nw).zoom)
		Next i
		
		'Paint wheel positions and collision information
		If nw = 1 Then
			For i As Byte = 1 To 4
				Circle (par.wheel(i).x * carscale(3) * w3d(nw).zoom + w3d(nw).xc, _
					par.wheel(i).z * carscale(3) * w3d(nw).zoom + w3d(nw).yc), 3, RGB(250, 120, 0), , , , F
			Next i
			Circle (w3d(nw).xc, w3d(nw).yc), par.box(4) * carscale(1) * w3d(nw).zoom, RGB(250, 120, 0)
			Line (w3d(nw).xc - par.box(1) * carscale(1) * w3d(nw).zoom, _
				w3d(nw).yc - par.box(3) * carscale(1) * w3d(nw).zoom)- _
				(w3d(nw).xc + par.box(1) * carscale(1) * w3d(nw).zoom, _
				w3d(nw).yc + par.box(3) * carscale(1) * w3d(nw).zoom), RGB(250, 120, 0), B
		ElseIf nw = 2 Then
			Line (w3d(nw).xc - par.box(1) * carscale(1) * w3d(nw).zoom, _
				w3d(nw).yc - par.box(2) * carscale(1) * w3d(nw).zoom)- _
				(w3d(nw).xc + par.box(1) * carscale(1) * w3d(nw).zoom, _
				w3d(nw).yc), RGB(250, 120, 0), B
		Else
			Line (w3d(nw).xc - par.box(3) * carscale(1) * w3d(nw).zoom, _
				w3d(nw).yc - par.box(2) * carscale(1) * w3d(nw).zoom)- _
				(w3d(nw).xc + par.box(3) * carscale(1) * w3d(nw).zoom, _
				w3d(nw).yc), RGB(250, 120, 0), B
		End If
		
		wfcar = car(curmodel)	'Warning: this line is slow
		If nw >= 2 Then
			For i As Short = 0 To wfcar.vs - 1
				Swap wfcar.v(i).y, wfcar.v(i).z
				If nw = 3 Then Swap wfcar.v(i).x, wfcar.v(i).z
			Next i
		End If
		
		For i As Short = 0 To wfcar.ps - 1
			Select Case wfcar.p(i).sel
				Case 0 : c = RGB(0, 250, 0)
				Case 1 : c = RGB(250, 0, 250)
				Case 2 : c = RGB(150, 250, 150)
				Case Else : c = RGB(250, 150, 250)
			End Select
			
			Select Case wfcar.p(i).kind
				Case 2 To 10
					v = wfcar.v(wfcar.p(i).vp(0))
					lx1 = v.x * w3d(nw).zoom + w3d(nw).xc
					ly1 = w3d(nw).yc - v.y * w3d(nw).zoom
					PSet (lx1, ly1), c
					For j As Short = 1 To wfcar.p(i).kind - 1
						v = wfcar.v(wfcar.p(i).vp(j))
						lx2 = v.x * w3d(nw).zoom + w3d(nw).xc
						ly2 = w3d(nw).yc - v.y * w3d(nw).zoom
						Line -(lx2, ly2), c
					Next j
					If wfcar.p(i).kind > 2 Then	Line -(lx1, ly1), c
				Case 11
					v = wfcar.v(wfcar.p(i).vp(0))
					w = wfcar.v(wfcar.p(i).vp(1))
					r = Sqr((v.x - w.x) ^ 2 + (v.y - w.y) ^ 2) * w3d(nw).zoom
					lx1 = v.x * w3d(nw).zoom + w3d(nw).xc
					ly1 = w3d(nw).yc - v.y * w3d(nw).zoom
					
					Circle (lx1, ly1), r, c
				Case 12
					If nw = 3 Then
						v = wfcar.v(wfcar.p(i).vp(0))
						w = wfcar.v(wfcar.p(i).vp(1))
						r = Sqr((v.x - w.x) ^ 2 + (v.y - w.y) ^ 2) * w3d(nw).zoom
						lx1 = v.x * w3d(nw).zoom + w3d(nw).xc
						ly1 = w3d(nw).yc - v.y * w3d(nw).zoom
						Circle (lx1, ly1), r, c
						
						w = wfcar.v(wfcar.p(i).vp(2))
						r = Sqr((v.x - w.x) ^ 2 + (v.y - w.y) ^ 2) * w3d(nw).zoom
						lx1 = v.x * w3d(nw).zoom + w3d(nw).xc
						ly1 = w3d(nw).yc - v.y * w3d(nw).zoom
						Circle (lx1, ly1), r, c
					ElseIf nw = 2 Then
						v = wfcar.v(wfcar.p(i).vp(0))
						w = wfcar.v(wfcar.p(i).vp(1))
						r = Sqr((v.x - w.x) ^ 2 + (v.y - w.y) ^ 2 + (v.z - w.z) ^ 2) * w3d(nw).zoom
						lx1 = v.x * w3d(nw).zoom + w3d(nw).xc
						ly1 = w3d(nw).yc - v.y * w3d(nw).zoom
						w = wfcar.v(wfcar.p(i).vp(3))
						lx2 = w.x * w3d(nw).zoom + w3d(nw).xc
						ly2 = w3d(nw).yc - w.y * w3d(nw).zoom
						Line (lx1, ly1 - r)-(lx2, ly1 + r), c, B
					ElseIf nw = 1 Then
						v = wfcar.v(wfcar.p(i).vp(0))
						w = wfcar.v(wfcar.p(i).vp(1))
						r = Sqr((v.x - w.x) ^ 2 + (v.y - w.y) ^ 2 + (v.z - w.z) ^ 2) * w3d(nw).zoom
						lx1 = v.x * w3d(nw).zoom + w3d(nw).xc
						ly1 = w3d(nw).yc - v.y * w3d(nw).zoom
						w = wfcar.v(wfcar.p(i).vp(3))
						lx2 = w.x * w3d(nw).zoom + w3d(nw).xc
						ly2 = w3d(nw).yc - w.y * w3d(nw).zoom
						Line (lx1, ly1 - r)-(lx2, ly1 + r), c, B
					End If
			End Select
		Next i
	End If
	
	View Screen
End Sub


Sub PJSelection
	Dim As Byte current, excurrent
	Dim As Integer xm, ym, wm, bm
	Dim akey As String
	
	Line (735, 42)-(954, 570), B_COL, BF
	DrawSection 740, 50, 949, 565, "Paint jobs"
	
	For i As Byte = 1 To pjcols
		For j As Byte = 1 To 4
			Line (765 + 40 * (j - 1), 70 + 16 * (i - 1))- Step (37, 14), material(pjcol(i, j)).c, BF
		Next j
	Next i
	
	Do
		GetMouse xm, ym, wm, bm
	Loop Until bm = 0
	
	Do
		GetMouse xm, ym, wm, bm
		
		If xm >= 765 AndAlso xm < 925 AndAlso ym >= 70 AndAlso ym < 70 + 16 * pjcols Then
			current = (ym - 70) \ 16 + 1
		Else
			current = 0
		End If
		
		If current <> excurrent Then
			If excurrent Then Line (764, 69 + 16 * (excurrent - 1))- Step (159, 16), B_COL, B
			If current Then Line (764, 69 + 16 * (current - 1))- Step (159, 16), RGB(250, 250, 0), B
			excurrent = current
		End If
	
		akey = InKey
		If akey = Chr(27) Or bm = 2 Then current = 0 : Exit Do
		If bm = 1 AndAlso current <> 0 Then Exit Do
	Loop
	
	If current Then		'A paint job was selected!
		Dim previous As UByte, amount(0 To 10) As Short
		Dim max As Short
	
		'Determine model previous colour
		For i As Short = 4 To car(curmodel).ps - 1
			Select Case car(curmodel).p(i).m(curpj)
				Case 200 To 203
					'If any undertermined materials exists, then
					'colour is undertermined
					previous = 200
					Exit For
				Case 48 To 91
					amount((car(curmodel).p(i).m(curpj) - 48) \ 4) += 1
			End Select
		Next i
		
		'It'll be the one that's most common
		If previous = 0 Then
			For i As Byte = 0 To 10
				If amount(i) > max Then
					max = amount(i)
					previous = 48 + 4 * i
				End If
			Next i
		End If
		
		'Now replace with new colour
		For i As Short = 4 To car(curmodel).ps - 1
			Dim v As UByte
			
			v = car(curmodel).p(i).m(curpj)
			If v >= previous And v < previous + 4 Then
				car(curmodel).p(i).m(curpj) = pjcol(current, v - previous + 1)
			End If
		Next i
	End If
	
	Do
		GetMouse xm, ym, wm, bm
	Loop Until bm = 0
	
	Line (735, 42)-(954, 570), B_COL, BF
End Sub


Sub AutoShade
	Dim As Vertex u, v, w, n
	Dim r As Double
	
	For i As Short = 4 To car(curmodel).ps - 1
		'Only process polygons
		If car(curmodel).p(i).kind >= 3 And car(curmodel).p(i).kind <= 10 Then
			'Calculate surface vertices
			u = car(curmodel).v(car(curmodel).p(i).vp(0))
			v = car(curmodel).v(car(curmodel).p(i).vp(1))
			w = car(curmodel).v(car(curmodel).p(i).vp(2))
			
			'...relative to pivot (v - u and w - u)
			v.x -= u.x : w.x -= u.x
			v.y -= u.y : w.y -= u.y
			v.z -= u.z : w.z -= u.z
			
			'Calculate normal (v x w)
			n.x = v.y * w.z - v.z * w.y
			n.y = v.z * w.x - v.x * w.z
			n.z = v.x * w.y - v.y * w.x
			
			'Normalise normal
			r = Sqr(n.x ^ 2 + n.y ^ 2 + n.z ^ 2)
			'n.x /= r : n.y /= r (not necessary to calculate)
			n.z /= -r	'It's inverted for some reason
			
			'Calculate dot product with (0, 0, 1),
			'which is n.z, so no need to do it
			
			'Store correct brightness
			Select Case n.z
				Case Is < .25
					car(curmodel).p(i).m(curpj) = 200
				Case .25 To .6
					car(curmodel).p(i).m(curpj) = 201
				Case .6 To .85
					car(curmodel).p(i).m(curpj) = 202
				Case Else
					car(curmodel).p(i).m(curpj) = 203
			End Select
		End If
	Next i
End Sub


Sub GenerateInfo (collision As Byte = 0, wheels As Byte = 0)
	Dim As Double minx, maxx, miny, maxy, minz, maxz
	
	If collision Then
		For i As Short = 0 To car(1).vs - 1
			If car(1).v(i).x < minx Then minx = car(1).v(i).x
			If car(1).v(i).x > maxx Then maxx = car(1).v(i).x
			If car(1).v(i).y < miny Then miny = car(1).v(i).y
			If car(1).v(i).y > maxy Then maxy = car(1).v(i).y
			If car(1).v(i).z < minz Then minz = car(1).v(i).z
			If car(1).v(i).z > maxz Then maxz = car(1).v(i).z
		Next i
		If minz > 0 Then minz = 0
		
		If maxx > Abs(minx) Then
			par.box(1) = maxx / carscale(1)
		Else
			par.box(1) = Abs(minx) / carscale(1)
		End If
		
		If maxy > Abs(miny) Then
			par.box(3) = maxy / carscale(1)
		Else
			par.box(3) = Abs(miny) / carscale(1)
		End If
		
		par.box(2) = maxz / carscale(1)
		par.box(4) = 1.05 * Sqr(maxx ^ 2 + maxy ^ 2 + maxz ^ 2) / carscale(1)
	End If
	
	If wheels Then
		par.wheel(1).x = (car(1).v(car(1).p(0).vp(0)).x + car(1).v(car(1).p(0).vp(3)).x) / (2 * carscale(3))
		par.wheel(1).z = car(1).v(car(1).p(0).vp(3)).y / carscale(3)
		par.wheel(2).x = (car(1).v(car(1).p(1).vp(0)).x + car(1).v(car(1).p(1).vp(3)).x) / (2 * carscale(3))
		par.wheel(2).z = car(1).v(car(1).p(1).vp(3)).y / carscale(3)
		par.wheel(3).x = par.wheel(2).x
		par.wheel(3).z = -par.wheel(2).z
		par.wheel(4).x = par.wheel(1).x
		par.wheel(4).z = -par.wheel(1).z
	End If
End Sub


Sub MoveCar (dx As Double, dy As Double, dz As Double)
	'Move bounding box
	For i As Short = 0 To 7
		car(curmodel).v(i).x += dx
		car(curmodel).v(i).y += dy
		car(curmodel).v(i).z += dz
	Next i
	
	'Only move these vertices if the model has no wheels (car2)
	If car(curmodel).p(0).kind <> 12 Then
		For i As Short = 8 To 19
			car(curmodel).v(i).x += dx
			car(curmodel).v(i).y += dy
			car(curmodel).v(i).z += dz
		Next i
	End If
	
	'Only move these vertices if the model is two-wheeled
	If car(curmodel).p(2).kind <> 12 Then
		For i As Short = 20 To 31
			car(curmodel).v(i).x += dx
			car(curmodel).v(i).y += dy
			car(curmodel).v(i).z += dz
		Next i
	End If
	
	'Move the rest of the car
	For i As Short = 32 To car(curmodel).vs - 1
		car(curmodel).v(i).x += dx
		car(curmodel).v(i).y += dy
		car(curmodel).v(i).z += dz
	Next i
End Sub


Sub MoveWheels (dx As Double, dy As Double, dz As Double)
	Dim As Vertex v, w
	
	For i As Short = 0 To car(curmodel).ps - 1
		If car(curmodel).p(i).kind = 12 Then	'A wheel
			v = car(curmodel).v(car(curmodel).p(i).vp(0))
			If v.x > 0 Then
				For j As Byte = 0 To 5
					car(curmodel).v(car(curmodel).p(i).vp(j)).x += dx
				Next j
			ElseIf v.x < 0 Then
				For j As Byte = 0 To 5
					car(curmodel).v(car(curmodel).p(i).vp(j)).x -= dx
				Next j
			End If
			If v.y > 0 Then
				For j As Byte = 0 To 5
					car(curmodel).v(car(curmodel).p(i).vp(j)).y += dy
				Next j
			ElseIf v.y < 0 Then
				For j As Byte = 0 To 5
					car(curmodel).v(car(curmodel).p(i).vp(j)).y -= dy
				Next j
			End If
			For j As Byte = 0 To 5
				car(curmodel).v(car(curmodel).p(i).vp(j)).z += dz
			Next j
		End If
	Next i
End Sub


Sub ResizeWheels (dw As Double, drx As Double, dri As Double)
	'dw = Delta width
	'drx = Delta external radius
	'dri = Delta internal radius
	Dim As Double w, rx, ri, basex
	Dim As Vertex u(0 To 5)
	
	For i As Short = 0 To car(curmodel).ps - 1
		If car(curmodel).p(i).kind = 12 Then	'A wheel
			'Obtain parameters
			For j As Byte = 0 To 5
				u(j) = car(curmodel).v(car(curmodel).p(i).vp(j))
			Next j
			
			w = Abs(u(0).x - u(3).x)
			rx = Sqr((u(0).x - u(1).x) ^ 2 + (u(0).y - u(1).y) ^ 2 + (u(0).z - u(1).z) ^ 2)
			ri = Sqr((u(0).x - u(2).x) ^ 2 + (u(0).y - u(2).y) ^ 2 + (u(0).z - u(2).z) ^ 2)
			
			'Add displacements
			w += dw
			rx += drx
			ri += dri
			
			'See which is internal
			If u(0).x > 0 Then
				If u(0).x > u(3).x Then
					basex = u(3).x
				Else
					basex = u(0).x
				End If
			Else
				If u(0).x > u(3).x Then
					basex = u(0).x
				Else
					basex = u(3).x
				End If
			End If
						
			'Render new wheel
			u(0).x = basex
			u(1).x = u(0).x
			u(1).y = u(0).y + rx
			u(1).z = u(0).z
			u(2).x = u(0).x
			u(2).y = u(0).y
			u(2).z = u(0).z + ri
			For j As Byte = 0 To 2
				If u(0).x > 0 Then
					u(j + 3).x = u(j).x + w
				Else
					u(j + 3).x = u(j).x - w
				End If
				u(j + 3).y = u(j).y
				u(j + 3).z = u(j).z
			Next j			

			'Make correction for wheel orientation
			If u(0).x < 0 Then
				Swap u(0), u(3)
				Swap u(1), u(4)
				Swap u(2), u(5)
			End If

			'Transfer to vertices
			For j As Byte = 0 To 5
				car(curmodel).v(car(curmodel).p(i).vp(j)) = u(j)
			Next j
			
			'~ 'Change width towards the inside
			'~ If car(curmodel).v(car(curmodel).p(i).vp(3)).x > 0 Then
				'~ For j = 3 To 5
					'~ car(curmodel).v(car(curmodel).p(i).vp(j)).x -= dw
				'~ Next j
			'~ Else
				'~ For j = 3 To 5
					'~ car(curmodel).v(car(curmodel).p(i).vp(j)).x += dw
				'~ Next j
			'~ End If
		End If
	Next i
End Sub


Sub GenerateWheels (where As Byte = curmodel)
	Dim u As Vertex
	
	'Make first four primitives wheels
	For i As Short = 0 To 3
		car(where).p(i).kind = 12
		car(where).p(i).flags = 0
		car(where).p(i).bcull = -1
		car(where).p(i).fcull = -1
		For j As Short = 0 To 5
			car(where).p(i).vp(j) = 6 * i + j + 8
		Next j
		For j As Byte = 0 To 15
			car(where).p(i).m(j) = 38
		Next j
	Next i
	
	'Coordinates for first wheel
	u.x = -.6
	u.y = -1.3
	u.z = .22
	For i As Short = 8 To 13
		car(where).v(i) = u
	Next i
	
	'Next wheel
	u.x = -u.x
	For i As Short = 14 To 19
		car(where).v(i) = u
	Next i
	
	'And the next
	u.y = -u.y
	For i As Short = 20 To 25
		car(where).v(i) = u
	Next i
	
	'And the final one
	u.x = -u.x
	For i As Short = 26 To 31
		car(where).v(i) = u
	Next i
	
	'Make sure wheels are produced
	If car(where).ps < 4 Then car(where).ps = 4
	If car(where).vs < 32 Then car(where).vs = 32
	
	'Now make these point-wheels real
	ResizeWheels .2, .22, .18
End Sub


Sub GenerateBoundingBox (model As Byte)
	Dim As Double minx, maxx, miny, maxy, minz, maxz
	
	For i As Short = 8 To car(model).vs - 1
		If car(model).v(i).x < minx Then minx = car(model).v(i).x
		If car(model).v(i).x > maxx Then maxx = car(model).v(i).x
		If car(model).v(i).y < miny Then miny = car(model).v(i).y
		If car(model).v(i).y > maxy Then maxy = car(model).v(i).y
		If car(model).v(i).z < minz Then minz = car(model).v(i).z
		If car(model).v(i).z > maxz Then maxz = car(model).v(i).z
	Next i
	
	'Original cars always use this. If something goes
	'wrong, we may try changing it (minz = 0)
	minz = 0
	
	car(model).v(0).x = minx
	car(model).v(0).y = maxy
	car(model).v(0).z = minz
	car(model).v(1).x = maxx
	car(model).v(1).y = maxy
	car(model).v(1).z = minz
	car(model).v(2).x = minx
	car(model).v(2).y = miny
	car(model).v(2).z = minz
	car(model).v(3).x = maxx
	car(model).v(3).y = miny
	car(model).v(3).z = minz
	car(model).v(4).x = minx
	car(model).v(4).y = maxy
	car(model).v(4).z = maxz
	car(model).v(5).x = maxx
	car(model).v(5).y = maxy
	car(model).v(5).z = maxz
	car(model).v(6).x = minx
	car(model).v(6).y = miny
	car(model).v(6).z = maxz
	car(model).v(7).x = maxx
	car(model).v(7).y = miny
	car(model).v(7).z = maxz
End Sub


Sub GenerateDebris
	Dim t As Double
	
	t = Timer
	
	For i As Byte = 0 To 3
		Dim n As Short
		
		Do
			n = Int(Rnd * car(1).ps)
			
			'Time-out after 2 seconds
			If Timer >= t + 2 Then Exit For
		Loop Until car(1).p(n).kind >= 2 And car(1).p(n).kind <= 5
	
		cexp(i).ps = 1
		cexp(i).p(0).flags = 0
		cexp(i).p(0).kind = car(1).p(n).kind
		cexp(i).vs = car(1).p(n).kind
		For j As Byte = 0 To car(1).p(n).kind - 1
			cexp(i).p(0).vp(j) = j
			cexp(i).v(j) = car(1).v(car(1).p(n).vp(j))
			'This For should be outside the For j, but
			'the result should be the same
			For k As Byte = 0 To paintjobs - 1
				cexp(i).p(0).m(k) = car(1).p(n).m(k)
			Next k
		Next j
	Next i
End Sub


Sub Edit3D
	Dim As Integer xm, ym, wm, bm
	Dim As Integer exxm, exym, exwm, exbm
	Dim As Byte updateall = -1, update = -1, updateinfo = -1
	Dim thiswindow As Byte, uwin As Double, vwin As Double
	Dim movemode As Byte = 0, mdist As Double = .1
	Dim oldwindow As Byte = -1
	
	curmodel = 0
	
	Dim akey As String
	Do
		If updateall Then
			ScreenLock
			Line (0, 0)-(959, 599), B_COL, BF
			Draw String (16, 16), "= 3D EDITOR =", WHITE_COL

			For i As Byte = 1 To 4
				DrawSection w3d(i).x1, w3d(i).y1, w3d(i).x2, w3d(i).y2, w3d(i).title, thiswindow = i
			Next i
			ScreenUnlock
			updateall = 0
			update = -1
			updateinfo = -1
		End If
		If update Then
			ScreenLock
			If update = -1 Then
				For i As Byte = 1 To 4
					If w3d(i).active Then Update3DWindow i
				Next i
			Else
				If w3d(update).active Then Update3DWindow update
			End If
			ScreenUnlock
			update = 0
		End If
		If updateinfo Then
			Dim c As ULong
			
			ScreenLock
			DrawSection 740, 50, 949, 565, "Info panel"
			Line (760, 64)- Step (171, 143), B_COL, BF
			Draw String (760, 64), "Model", GRAY_COL
			
			Dim text As String
			Select Case curmodel
				Case 0: text = "Showroom"
				Case 1: text = "Game"
				Case 2: text = "Intro"
				Case 3 To 4: text = "Aux " & curmodel - 2
			End Select
			Draw String (860, 64), text, RGB(0, 180, 100)
			
			Draw String (760, 80), "Paint job", GRAY_COL
			Draw String (860, 80), (curpj + 1) & "/" & paintjobs, RGB(0, 180, 100)
			
			Draw String (760, 96), "Vertices", GRAY_COL
			If car(curmodel).vs <= 255 Then c = RGB(0, 180, 100) Else c = RGB(200, 180, 0)
			Draw String (860, 96), Str(car(curmodel).vs), c
			
			Draw String (760, 112), "Primitives", GRAY_COL
			If car(curmodel).ps <= 255 Then c = RGB(0, 180, 100) Else c = RGB(200, 180, 0)
			Draw String (860, 112), Str(car(curmodel).ps), c
			
			Dim mm As String
			Draw String (760, 144), "Move mode", GRAY_COL
			Select Case movemode
				Case 0 : mm = "Car"
				Case 1 : mm = "Wheels"
				Case 2 : mm = "Resize W."
				Case 3 : mm = "Collision"
			End Select
			Draw String (860, 144), mm, RGB(0, 180, 100)
			
			Draw String (760, 160), "Move snap", GRAY_COL
			Draw String (860, 160), mdist & "m", RGB(0, 180, 100)
			
			ScreenUnlock
			updateinfo = 0
		End If
		
		GetMouse xm, ym, wm, bm
		If xm <> exxm OrElse ym <> exym OrElse wm <> exwm OrElse exbm <> exbm Then
			thiswindow = 0
			For i As Byte = 1 To 4
				If w3d(i).active Then
					If xm >= w3d(i).x1 AndAlso xm <= w3d(i).x2 AndAlso ym >= w3d(i).y1 AndAlso ym <= w3d(i).y2 Then
						thiswindow = i
						uwin = (xm - w3d(i).xc) / w3d(i).zoom
						vwin = (w3d(i).yc - ym) / w3d(i).zoom
						If oldwindow <> thiswindow Then
							updateall = -1
							oldwindow = thiswindow
						End If
						Exit For
					End If
				End If
			Next i
			
			'Zoom in and out
			If thiswindow <> 0 AndAlso wm <> exwm Then
				w3d(thiswindow).zoom *= 1.1 ^ (wm - exwm)
				update = thiswindow
			End If
			
			exxm = xm : exym = ym : exbm = bm : exwm = wm
		End If
		
		If MaintainMenu < 0 Then Exit Do
		akey = InKey
	
		Select Case akey
			Case Chr(255, 77)
				Select Case thiswindow
					Case 1, 2
						If movemode = 1 Then
							MoveWheels mdist, 0, 0
						ElseIf movemode = 2 Then
							ResizeWheels mdist / 4, 0, 0
						ElseIf movemode = 0 Then
							MoveCar mdist, 0, 0
						Else
							If par.box(1) < 50 Then par.box(1) += 1
						End If
						update = -1
					Case 3
						If movemode = 1 Then
							MoveWheels 0, mdist / 4, 0
						ElseIf movemode = 2 Then
							ResizeWheels 0, 0, mdist / 4
						ElseIf movemode = 0 Then
							MoveCar 0, mdist, 0
						Else
							If par.box(3) < 80 Then par.box(3) += 1
						End If
						update = -1
					Case Else
						curyaw += PI / 16
						update = 4
				End Select
			Case Chr(255, 75)
				Select Case thiswindow
					Case 1, 2
						If movemode = 1 Then
							MoveWheels -mdist, 0, 0
						ElseIf movemode = 2 Then
							ResizeWheels -mdist / 4, 0, 0
						ElseIf movemode = 0 Then
							MoveCar -mdist, 0, 0
						Else
							If par.box(1) > 1 Then par.box(1) -= 1
						End If
						update = -1
					Case 3
						If movemode = 1 Then
							MoveWheels 0, -mdist, 0
						ElseIf movemode = 2 Then
							ResizeWheels 0, 0, -mdist / 4
						ElseIf movemode = 0 Then
							MoveCar 0, -mdist, 0
						Else
							If par.box(3) > 1 Then par.box(3) -= 1
						End If
						update = -1
					Case Else
						curyaw -= PI / 16
						update = 4
				End Select
			Case Chr(255, 80)
				Select Case thiswindow
					Case 1
						If movemode = 1 Then
							MoveWheels 0, -mdist, 0
						ElseIf movemode = 2 Then
							ResizeWheels 0, -mdist / 4, -mdist / 4
						ElseIf movemode = 0 Then
							MoveCar 0, -mdist, 0
						Else
							If par.box(3) > 1 Then par.box(3) -= 1
						End If
						update = -1
					Case 2, 3
						If movemode = 1 Then
							MoveWheels 0, 0, -mdist
						ElseIf movemode = 2 Then
							ResizeWheels 0, -mdist / 4, 0
						ElseIf movemode = 0 Then
							MoveCar 0, 0, -mdist
						Else
							If par.box(2) > 1 Then par.box(2) -= 1
						End If
						update = -1
					Case Else
						curpitch -= PI / 32
						update = 4
				End Select
			Case Chr(255, 72)
				Select Case thiswindow
					Case 1
						If movemode = 1 Then
							MoveWheels 0, mdist, 0
						ElseIf movemode = 2 Then
							ResizeWheels 0, mdist / 4, mdist / 4
						ElseIf movemode = 0 Then
							MoveCar 0, mdist, 0
						Else
							If par.box(3) < 80 Then par.box(3) += 1
						End If
						update = -1
					Case 2, 3
						If movemode = 1 Then
							MoveWheels 0, 0, mdist
						ElseIf movemode = 2 Then
							ResizeWheels 0, mdist / 4, 0
						ElseIf movemode = 0 Then
							MoveCar 0, 0, mdist
						Else
							If par.box(2) < 40 Then par.box(2) += 1
						End If
						update = -1
					Case Else
						curpitch += PI / 32
						update = 4
				End Select
			Case Chr(9)		'Set move mode
				If MultiKey(&H1D) Then
					movemode = (movemode + 3) Mod 4
				Else
					movemode = (movemode + 1) Mod 4
				End If
				updateinfo = -1
			Case "p", "P", "."	'Cycle through paint-jobs
				curpj += 1
				If curpj = paintjobs Then curpj = 0
				update = -1
				updateinfo = -1
			Case ","		'Cycle paint-jobs backwards
				If curpj = 0 Then
					curpj = paintjobs - 1
				Else
					curpj -= 1
				End If
				update = -1
				updateinfo = -1
			Case "m", "M"	'Cycle models
				curmodel = (curmodel + 1) Mod 5
				update = -1
				updateinfo = -1
			Case "1" To "5"
				curmodel = ValInt(akey) - 1
				update = -1
				updateinfo = -1
			Case Chr(3)	'Copy current model to clipboard
				carclip = car(curmodel)
			Case Chr(22)	'Copy clipboard to current model
				car(curmodel) = carclip
				update = -1
			Case Chr(16)	'CTRL+P - New paintjob
				If paintjobs < 16 Then
					paintjobs += 1
					For i As Byte = 0 To 2
						For j As Short = 0 To car(i).ps - 1
							car(i).p(j).m(paintjobs - 1) = car(i).p(j).m(curpj)
						Next j
					Next i
					curpj = paintjobs - 1
					update = -1
					updateinfo = -1
				End If
			Case Chr(15)	'CTRL+O - Delete paintjob
				If paintjobs > 1 Then
					If curpj < paintjobs - 1 Then
						For i As Byte = 0 To 2
							For j As Short = 0 To car(i).ps - 1
								For k As Byte = curpj To paintjobs - 2
									car(i).p(j).m(k) = car(i).p(j).m(k + 1)
								Next k
							Next j
						Next i
					End if
					paintjobs -= 1
					If curpj >= paintjobs Then curpj = paintjobs - 1
					update = -1
					updateinfo = -1
				End If
			Case Chr(13)
				If thiswindow = 4 Then	'Select paint job
					PJSelection
					
					updateinfo = -1
					update = -1
				End If
			Case Chr(1)		'Autoshade
				AutoShade
				update = 4
			Case Chr(2)		'Auto-generate collision and wheel info
				GenerateInfo -1, -1
				update = -1
			Case Chr(4)		'CTRL+D - Generate debris
				GenerateDebris
			Case Chr(23)	'CTRL+W - Generate wheels
				GenerateWheels
				update = -1
			Case "`"	'Debugging key
				Open "coso.txt" For Output As 1
				For i As Byte = 0 To 3
					For j As Byte = 0 To 15
						Print #1, car(curmodel).p(i).m(j); " ";
					Next j
					Print #1,
				Next i
				Close 1
			Case "*"	'Move faster
				If mdist < 1 Then
					mdist *= 2
					updateinfo = -1
				End If
			Case "/"	'Move slower
				If mdist > 0.01 Then
					mdist /= 2
					updateinfo = -1
				End If
			Case Chr(19)	'CTRL+S - Scale model
				Dim scline As String
				Dim As Byte dox, doy, doz
				
				Draw String (760, 192), "Scale", WHITE_COL
				
				Dim sc As InputValue
				sc.x = 852 : sc.y = 192
				sc.min = -100 : sc.max = 100 : sc.maxlength = 8
				sc.fpt = -1 : sc.allowed = "xyzXYZ"
				NumInput(sc)
				
				scline = sc.s
				scline = UCase(Trim(scline))
				If InStr(scline, "X") Then dox = -1
				If InStr(scline, "Y") Then doy = -1
				If InStr(scline, "Z") Then doz = -1
				If dox + doy + doz = 0 Then dox = -1 : doy = -1 : doz = -1
				
				For i As Short = 0 To car(curmodel).vs - 1
					If dox Then car(curmodel).v(i).x *= Val(scline)
					If doy Then car(curmodel).v(i).y *= Val(scline)
					If doz Then car(curmodel).v(i).z *= Val(scline)
				Next i
				update = -1 : updateinfo = -1
			Case Chr(21)	'CTRL+U - Reset culling for current model
				For i As Short = 0 To car(curmodel).ps - 1
					car(curmodel).p(i).bcull = -1
					car(curmodel).p(i).fcull = -1
				Next i
			Case "["	'Highlight previous primitive
				Dim curhigh As Short
				
				curhigh = car(curmodel).ps
				For i As Short = 0 To car(curmodel).ps - 1
					If car(curmodel).p(i).sel And 2 Then curhigh = i : Exit For
				Next i
				
				If curhigh >= 0 Then car(curmodel).p(curhigh).sel And= 1
				curhigh -= 1
				If curhigh >= 0 Then car(curmodel).p(curhigh).sel Or= 2
				update = -1
			Case "]"	'Highlight next primitive
				Dim curhigh As Short
				
				curhigh = -1
				For i As Short = 0 To car(curmodel).ps - 1
					If car(curmodel).p(i).sel And 2 Then curhigh = i : Exit For
				Next i
				
				If curhigh < car(curmodel).ps Then car(curmodel).p(curhigh).sel And= 1
				curhigh += 1
				If curhigh < car(curmodel).ps Then car(curmodel).p(curhigh).sel Or= 2
				update = -1
			Case " "	'Toggle highlighted primitive
				Dim curhigh As Short
				
				curhigh = -1
				For i As Short = 0 To car(curmodel).ps - 1
					If car(curmodel).p(i).sel And 2 Then curhigh = i : Exit For
				Next i
				
				If curhigh <> -1 Then
					car(curmodel).p(curhigh).sel XOr= 1
					update = -1
				End If
			Case Chr(27), Chr(255) & "k"
				curmenu = -1
				Exit Do
		End Select
	Loop

	Line (0, 0)-(959, 599), B_COL, BF
End Sub


Sub LoadPalette
	Dim f As Integer
	
	If Not FileExists(progdir & "stunts.col") Then Exit Sub
	
	f = FreeFile
	Open progdir & "stunts.col" For Binary Access Read As f
	For i As Short = 0 To 255
		Get #f, , pal(i)
	Next i
	Close f
End Sub


Function rbm(id As String) As Byte
	For i As Byte = 1 To bitmaps
		If bitmap(i).id = id Then Return i
	Next i
	
	Return 0
End Function


Sub LoadVSH (finame As String)
	Dim f As Integer, chunks As Short, s As String
	Dim n As Short, p As ULong, d As Long
	
	File_File = finame
	File_ErNum = 0
	If Not FileExists(finame) Then
		File_Error = "File not found"
		File_ErNum = 1
		Exit Sub
	End If
	
	f = FreeFile
	Open finame For Binary Access Read As f
	Get #f, 5, chunks
	
	s = Space(4)
	For i As Short = 0 To chunks - 1
		Get #f, 4 * i + 7, s
		
		n = rbm(s)
		If n Then
			'Bitmap already loaded, remove to replace
			Deallocate bitmap(n).i
			ImageDestroy bitmap(n).i2
			ImageDestroy bitmap(n).i3
			
			bitmap(n).i = 0
			bitmap(n).i2 = 0
			bitmap(n).i3 = 0
			bitmap(n).id = ""
			If bitmaps <> n Then Swap bitmap(n), bitmap(bitmaps)
			bitmaps -= 1
		End If
		
		'Load new bitmap
		Get #f, 4 * (i + chunks) + 7, p
		Seek #f, p + 8 * chunks + 7
		
		bitmaps += 1
		bitmap(bitmaps).id = s
		Get #f, , bitmap(bitmaps).w
		Get #f, , bitmap(bitmaps).h
		Get #f, , bitmap(bitmaps).uw(1)
		Get #f, , bitmap(bitmaps).uw(2)
		Get #f, , bitmap(bitmaps).x
		Get #f, , bitmap(bitmaps).y
		For j As Byte = 1 To 4
			Get #f, , bitmap(bitmaps).ub(j)
		Next j
		
		bitmap(bitmaps).i = Allocate(bitmap(bitmaps).w * bitmap(bitmaps).h)
		Get #f, , *bitmap(bitmaps).i, bitmap(bitmaps).w * bitmap(bitmaps).h
		bitmap(bitmaps).i2 = ImageCreate(2 * bitmap(bitmaps).w, 2 * bitmap(bitmaps).h)
		bitmap(bitmaps).i3 = ImageCreate(3 * bitmap(bitmaps).w, 3 * bitmap(bitmaps).h)
		
		If InStr(maskmask, "/" & s & "/") Then
			bitmap(bitmaps).ismask = -1
			d = 0
			For y As Short = 0 To bitmap(bitmaps).h - 1
				For x As Short = 0 To bitmap(bitmaps).w - 1
					Line bitmap(bitmaps).i2, (2 * x, 2 * y)- Step (1, 1), IIf(bitmap(bitmaps).i[d], &HFFFFFFFF, 0), B
					Line bitmap(bitmaps).i3, (3 * x, 3 * y)- Step (2, 2), IIf(bitmap(bitmaps).i[d], &HFFFFFFFF, 0), BF
					d += 1
				Next x
			Next y
		Else
			bitmap(bitmaps).ismask = 0
			d = 0
			For y As Short = 0 To bitmap(bitmaps).h - 1
				For x As Short = 0 To bitmap(bitmaps).w - 1
					Line bitmap(bitmaps).i2, (2 * x, 2 * y)- Step (1, 1), pal(bitmap(bitmaps).i[d]).c, BF
					Line bitmap(bitmaps).i3, (3 * x, 3 * y)- Step (2, 2), pal(bitmap(bitmaps).i[d]).c, BF
					d += 1
				Next x
			Next y
		End If
		
		Select Case s
			Case "dast"
				bitmap(bitmaps).hasmask = -1
				bitmap(bitmaps).maskname = "dasm"
			Case "dot "
				bitmap(bitmaps).hasmask = -1
				bitmap(bitmaps).maskname = "dota"
			Case "ins1"
				bitmap(bitmaps).hasmask = -1
				bitmap(bitmaps).maskname = "inm1"
			Case "ins3"
				bitmap(bitmaps).hasmask = -1
				bitmap(bitmaps).maskname = "inm3"
			Case "gnob"
				bitmap(bitmaps).hasmask = -1
				bitmap(bitmaps).maskname = "gnab"
			Case Else
				bitmap(bitmaps).hasmask = 0
		End Select
	Next i
	
	'Fix instrument coordinates
	n = rbm("ins2")
	For i As Byte = 1 To bitmaps
		Select Case bitmap(i).id
			Case "ins1", "ins3", "inm1", "inm3"
				bitmap(i).rx = bitmap(n).x + bitmap(i).x
				bitmap(i).ry = bitmap(n).y + bitmap(i).y
			Case Else
				bitmap(i).rx = bitmap(i).x
				bitmap(i).ry = bitmap(i).y
		End Select
	Next i
	Close f
	
	UpdateDashItems
End Sub


Sub MoveDashImageTo (bm As Byte, x As Short, y As Short)
	Dim dif As Short, n As Short
	
	If InStr("/ins1/ins3/inm1/inm3/", "/" & bitmap(bm).id & "/") Then
		'Update X, Y, RX and RY so that the whole bitmap
		'remains within INS2
		
		n = rbm("ins2")
		'Notice that, if INS2 is not loaded, an attempt
		'to move other instrument images will cause a
		'segmentation fault!  This can be prevented by
		'simply creating a bitmap(0) with its coordinates
		'equal to (0, 0). Useful for a background image
		'Width and height should be set to 320x200.
		
		bitmap(bm).x = x - bitmap(n).x
		If CShort(bitmap(bm).x) < 0 Then bitmap(bm).x = 0
		If bitmap(bm).x + bitmap(bm).w > bitmap(n).w Then bitmap(bm).x = bitmap(n).w - bitmap(bm).w
		bitmap(bm).rx = bitmap(bm).x + bitmap(n).x
		
		bitmap(bm).y = y - bitmap(n).y
		If CShort(bitmap(bm).y) < 0 Then bitmap(bm).y = 0
		If bitmap(bm).y + bitmap(bm).h > bitmap(n).h Then bitmap(bm).y = bitmap(n).h - bitmap(bm).h
		bitmap(bm).ry = bitmap(bm).y + bitmap(n).y
	Else
		'Update X, Y, RX and RY so that the whole bitmap
		'remains within the screen
		
		dif = bitmap(bm).rx - bitmap(bm).x
		bitmap(bm).rx = x
		If CShort(bitmap(bm).rx) < 0 Then bitmap(bm).rx = 0
		If bitmap(bm).rx + bitmap(bm).w > 320 Then bitmap(bm).rx = 320 - bitmap(bm).w
		bitmap(bm).x = bitmap(bm).rx - dif
		
		dif = bitmap(bm).ry - bitmap(bm).y
		bitmap(bm).ry = y
		If CShort(bitmap(bm).ry) < 0 Then bitmap(bm).ry = 0
		If bitmap(bm).ry + bitmap(bm).h > 200 Then bitmap(bm).ry = 200 - bitmap(bm).h
		bitmap(bm).y = bitmap(bm).ry - dif
	End If
	
	'Update sister images (masks)
	If bitmap(bm).hasmask Then
		n = rbm(bitmap(bm).maskname)
		If n Then
			bitmap(n).x = bitmap(bm).x
			bitmap(n).y = bitmap(bm).y
			bitmap(n).rx = bitmap(bm).rx
			bitmap(n).ry = bitmap(bm).ry
		End If
	End If
	
	'Update instruments if main bitmap moved
	If bitmap(bm).id = "ins2" Then
		n = rbm("ins1")
		If n Then
			bitmap(n).rx = bitmap(n).x + bitmap(bm).x
			bitmap(n).ry = bitmap(n).y + bitmap(bm).y
		End If
		n = rbm("ins3")
		If n Then
			bitmap(n).rx = bitmap(n).x + bitmap(bm).x
			bitmap(n).ry = bitmap(n).y + bitmap(bm).y
		End If
		n = rbm("inm1")
		If n Then
			bitmap(n).rx = bitmap(n).x + bitmap(bm).x
			bitmap(n).ry = bitmap(n).y + bitmap(bm).y
		End If
		n = rbm("inm3")
		If n Then
			bitmap(n).rx = bitmap(n).x + bitmap(bm).x
			bitmap(n).ry = bitmap(n).y + bitmap(bm).y
		End If
	End If
End Sub


Sub LoadBitmap (id As String, finame As String)
	File_File = finame
	File_ErNum = 0
	If Not FileExists(finame) Then
		File_Error = "File not found"
		File_ErNum = 1
		Exit Sub
	End If
	
	Dim n As Short, p As Colour Ptr
	Dim As Integer imgwidth, imgheight, imgbpp
	
	n = InStrRev(finame, ".")
	Select Case LCase(Mid(finame, n))
		Case ".tga" : p = TargaLoad(finame)
		Case ".bmp"
			#ifdef __USE_SOIL__
				p = LoadRGBAFile(finame)
			#else
				BLoad finame, p
			#endif
		Case Else
			#ifdef __USE_SOIL__
				p = LoadRGBAFile(finame)
			#else
				Dim s As String
				s = Config("imagick")
				If Len(s) Then
					s = s & " " & finame & " -depth 32 " & workdir & "temp.tga"
					Shell s
					p = TargaLoad(workdir & "temp.tga")
				Else
					Exit Sub
				End If
			#endif
	End Select
	
	If p = 0 Then Exit Sub
	ImageInfo p, imgwidth, imgheight, imgbpp
	If imgbpp <> 4 Then ImageDestroy p : Exit Sub
	If imgwidth > 320 Or imgheight > 200 Then Exit Sub
	
	n = rbm(id)
	
	If n Then
		Deallocate bitmap(n).i
		ImageDestroy bitmap(n).i2
		ImageDestroy bitmap(n).i3
		Swap bitmap(n), bitmap(bitmaps)
		bitmaps -= 1
	End If

	bitmaps += 1
	bitmap(bitmaps).id = id
	bitmap(bitmaps).x = 0
	bitmap(bitmaps).y = 0
	bitmap(bitmaps).rx = 0	'These two should be relative
	bitmap(bitmaps).ry = 0
	bitmap(bitmaps).w = imgwidth
	bitmap(bitmaps).h = imgheight
	bitmap(bitmaps).i = Allocate(imgwidth * imgheight)
	bitmap(bitmaps).i2 = ImageCreate(2 * imgwidth, 2 * imgheight)
	bitmap(bitmaps).i3 = ImageCreate(3 * imgwidth, 3 * imgheight)
	bitmap(bitmaps).ismask = (InStr(maskmask, "/" & id & "/") <> 0)
	bitmap(bitmaps).hasmask = (InStr(masked, "/" & id & ":") <> 0)
	If bitmap(bitmaps).hasmask Then _
		bitmap(bitmaps).maskname = Mid(masked, InStr(masked, "/" & id & ":") + 6, 4)
	
	
	Dim pp As Colour, minc As UByte, min As Short
	Dim d As ULong, di As ULong, dif As Short
	
	For y As Short = 0 To imgheight - 1
		For x As Short = 0 To imgwidth - 1
			pp = p[8 + d]
			min = 1000
			If bitmap(bitmaps).ismask Then
				If pp.a >= 128 Then
					bitmap(bitmaps).i[di] = 0
					Line bitmap(bitmaps).i2, (2 * x, 2 * y)- Step (1, 1), 0, B
					Line bitmap(bitmaps).i3, (3 * x, 3 * y)- Step (2, 2), 0, BF
				Else
					bitmap(bitmaps).i[di] = 255
					Line bitmap(bitmaps).i2, (2 * x, 2 * y)- Step (1, 1), &HFFFFFFFF&, B
					Line bitmap(bitmaps).i3, (3 * x, 3 * y)- Step (2, 2), &HFFFFFFFF&, BF
				End If
			Else
				For n = 0 To 255
					dif = Abs(pp.r - pal(n).r) + Abs(pp.g - pal(n).g) + Abs(pp.b - pal(n).b)
					If dif < min Then
						min = dif
						minc = n
					End If
				Next n
				bitmap(bitmaps).i[di] = minc
				Line bitmap(bitmaps).i2, (2 * x, 2 * y)- Step (1, 1), pal(minc).c, B
				Line bitmap(bitmaps).i3, (3 * x, 3 * y)- Step (2, 2), pal(minc).c, BF
			End If
			
			d += 1
			di += 1
		Next x
		If d Mod 4 Then d += (4 - (d Mod 4))
	Next y

	ImageDestroy p
	
	For n = 1 To UBound(dashitem)
		If dashitem(n).id = id Then
			dashitem(n).n = bitmaps
			Exit For
		End If
	Next n
	
	'Set the relative coordinates for instruments
	n = rbm("ins2")
	If n <> 0 AndAlso InStr("/ins1/ins3/inm1/inm3/", "/" & id & "/") <> 0 Then
		bitmap(bitmaps).rx += bitmap(n).x
		bitmap(bitmaps).ry += bitmap(n).y
	End If
	
	'If this is a bitmap that has a mask, also load
	'the mask from the same image. Recursive call.
	If bitmap(bitmaps).hasmask Then LoadBitmap bitmap(bitmaps).maskname, finame

	UpdateDashItems
End Sub


Sub RemoveBitmap (n As UByte)
	If n = 0 Or n > bitmaps Then Exit Sub
	
	If bitmap(n).hasmask Then RemoveBitmap rbm(bitmap(n).maskname)
		
	For i As Short = 1 To UBound(dashitem)
		If dashitem(i).id = bitmap(n).id Then
			dashitem(i).n = 0
			Exit For
		End If
	Next i
	
	Deallocate bitmap(n).i
	ImageDestroy bitmap(n).i2
	ImageDestroy bitmap(n).i3
	bitmap(n).id = ""
	Swap bitmap(n), bitmap(bitmaps)
	bitmaps -= 1
	
	UpdateDashItems
End Sub


Sub SaveBitmap (id As String, finame As String)
	Dim n As UByte, p As Colour Ptr, d As ULong
	Dim m As UByte, col As Colour
	
	n = rbm(id)
	If n = 0 Then Exit Sub
	
	If bitmap(n).hasmask Then m = rbm(bitmap(n).maskname)
	
	p = ImageCreate(bitmap(n).w, bitmap(n).h)
	d = 0
	For y As Short = 0 To bitmap(n).h - 1
		For x As Short = 0 To bitmap(n).w - 1
			col = pal(bitmap(n).i[d])
			If m Then col.a = 255 - bitmap(m).i[d]
			PSet p, (x, y), col.c
			d += 1
		Next x
	Next y
	
	n = InStrRev(finame, ".")
	Select Case LCase(Mid(finame, n))
		Case ".tga" : TargaSave finame, p
		Case ".bmp" : BSave finame, p
		Case ".png"
			#ifdef __USE_SOIL__
				SavePNGFile p, finame, True
			#else
				Dim s As String
				
				s = Config("imagick")
				If Len(s) Then
					TargaSave workdir & "temp.tga", p
					s = s & " " & workdir & "temp.tga -depth 32 " & finame
					Shell s
				End If
			#endif
		Case ".jpg", ".jpeg"
			Dim s As String
				
			s = Config("imagick")
			If Len(s) Then
				TargaSave workdir & "temp.tga", p
				s = s & " " & workdir & "temp.tga " & finame
				Shell s
			End If
	End Select
	
	ImageDestroy p
End Sub


Sub UpdateDashItems
	For i As Byte = 1 To UBound(dashitem)
		dashitem(i).n = 0
		For j As Byte = 1 To bitmaps
			If bitmap(j).id = dashitem(i).id Then _
				dashitem(i).n = j
		Next j
	Next i
End Sub


Sub GenerateSpokes (sp() As IntCoor2D, sps As Short, _
		centre As IntCoor2D, r As Double, arcstart As Double, _
		arcend As Double, ecc As Double, direc As Byte)
		
	Dim As Double astart, aend
	astart = arcstart
	aend = arcend
	If direc < 0 Then Swap astart, aend
	
	Dim t As Short = 0
		
	For i As Double = astart To aend Step (aend - astart) / sps
		If ecc >= 1 Then
			sp(t).x = r * Cos(i) / ecc + centre.x
			sp(t).y = centre.y - r * Sin(i)
		Else
			sp(t).x = r * Cos(i) + centre.x
			sp(t).y = centre.y - ecc * r * Sin(i)
		End If
		t += 1
		If t > sps Then Exit For
	Next i
End Sub


Sub DashManager
	Dim As Integer xm, ym, wm, bm
	Dim As Integer exxm, exym, exwm, exbm
	Dim akey As String
	Dim update As Byte = -1, n As Short
	Dim steerd As Byte = 0, exsteerd As Byte = 0
	Dim curbm As UByte = 1, dashzoom As Byte = 2
	
	Dim steer_r As Double = 60, steer_curspot As UByte = 0
	Dim steer_spots As UByte = 30, steer_ex As Double = 1
	Dim steer_cx As Short = 160, steer_cy As Short = 150
	
	Dim speed_r As Double = 40, speed_curspot As Byte = 0
	'Dim speed_spots As UByte = 103
	Dim As Double speed_ex = 1, speed_start = 5.8, speed_end = 3.5
	Dim speed_cx As Short = 100, speed_cy As Short = 150
	Dim speed_dir As Byte = 1
	
	Dim tac_r As Double = 40, tac_curspot As Byte = 0
	'Dim tac_spots As UByte = 127
	Dim As Double tac_ex = 1, tac_start = 5.8, tac_end = 3.5
	Dim tac_cx As Short = 170, tac_cy As Short = 150
	Dim tac_dir As Byte = 1
	
	Dim arccontrol As Byte = 0, curgear As Byte = 1
	
	'Op-Modes:
	'0 - Drive
	'1 - Images
	'2 - Steering wheel
	'3 - Speed-o-meter
	'4 - Tach-o-meter
	'5 - Gear box
	
	'Detect dash images
	UpdateDashItems
	
	Do
		If update Then
			ScreenLock
			Line (0, 0)-(959, 599), B_COL, BF
			Draw String (16, 16), "= DASH MANAGER =", WHITE_COL
			Select Case opmode
				Case 0	'Driving simulation
					If dashzoom = 2 Then
						DrawSection 47, 90, 692, 502, "Driving test"
						Line (50, 100)- Step (639, 399), RGB(0, 0, 0), BF
						View Screen (50, 100)-(689, 499)
					Else
						View Screen (0, 0)-(959, 599)
					End If
					n = rbm("roof")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dash")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dasm")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, And
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, And
						End If
					End If
					n = rbm("dast")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, Or
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, Or
						End If
					End If
					n = rbm("whl" & (2 + steerd))
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("inm" & (2 + steerd))
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).rx, 3 * bitmap(n).ry), bitmap(n).i3, And
						Else
							Put(2 * bitmap(n).rx + 50, 2 * bitmap(n).ry + 100), bitmap(n).i2, And
						End If
					End If
					n = rbm("ins" & (2 + steerd))
					If n Then
						If steerd Then
							If dashzoom = 3 Then
								Put(3 * bitmap(n).rx, 3 * bitmap(n).ry), bitmap(n).i3, Or
							Else
								Put(2 * bitmap(n).rx + 50, 2 * bitmap(n).ry + 100), bitmap(n).i2, Or
							End If
						Else
							If dashzoom = 3 Then
								Put(3 * bitmap(n).rx, 3 * bitmap(n).ry), bitmap(n).i3, PSet
							Else
								Put(2 * bitmap(n).rx + 50, 2 * bitmap(n).ry + 100), bitmap(n).i2, PSet
							End If
						End If
					End If
					View Screen
				Case 1	'Images
					If dashzoom = 2 Then
						DrawSection 47, 90, 692, 502, "Dash view"
						Line (50, 100)- Step (639, 399), RGB(0, 0, 0), BF
						View Screen (50, 100)-(689, 499)
					Else
						View Screen (0, 0)-(959, 599)
					End If
					n = rbm("roof")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dash")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dasm")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, And
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, And
						End If
					End If
					n = rbm("dast")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, Or
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, Or
						End If
					End If
					n = rbm("whl2")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = dashitem(curbm).n
					If n <> 0 AndAlso bitmap(n).i Then
						If dashzoom = 3 Then
							Put (3 * bitmap(n).rx, 3 * bitmap(n).ry), bitmap(n).i3, PSet
							Line (3 * bitmap(n).rx, 3 * bitmap(n).ry)- Step (3 * bitmap(n).w - 1, 3 * bitmap(n).h - 1), RGB(250, 250, 0), B
							Draw String (3 * bitmap(n).rx + 3, 3 * bitmap(n).ry + 1), bitmap(n).id, RGB(0, 0, 0)
							Draw String (3 * bitmap(n).rx + 2, 3 * bitmap(n).ry), bitmap(n).id, RGB(250, 250, 0)
						Else
							Put (2 * bitmap(n).rx + 50, 2 * bitmap(n).ry + 100), bitmap(n).i2, PSet
							Line (2 * bitmap(n).rx + 50, 2 * bitmap(n).ry + 100)- Step (2 * bitmap(n).w - 1, 2 * bitmap(n).h - 1), RGB(250, 250, 0), B
							Draw String (2 * bitmap(n).rx + 53, 2 * bitmap(n).ry + 101), bitmap(n).id, RGB(0, 0, 0)
							Draw String (2 * bitmap(n).rx + 52, 2 * bitmap(n).ry + 100), bitmap(n).id, RGB(250, 250, 0)
						End If
						
						View Screen
						If dashzoom = 2 Then
							Draw String (760, 390), "Coors", GRAY_COL
							Draw String (824, 390), "(" & bitmap(n).rx & ", " & bitmap(n).ry & ")", NUM_COL
							Draw String (760, 406), "Dims", GRAY_COL
							Draw String (824, 406), bitmap(n).w & "x" & bitmap(n).h, NUM_COL
							Draw String (760, 422), "Param", GRAY_COL
							Draw String (824, 422), bitmap(n).uw(1) & ", " & bitmap(n).uw(2), NUM_COL
						End If
					End If
					View Screen
					
					If dashzoom = 2 Then
						DrawSection 740, 90, 917, 355, "Images"
						DrawSection 740, 370, 917, 502, "Image info"
						Dim c As ULong
											
						For i As Byte = 1 To UBound(dashitem)
							If i = curbm Then Line (750, 105 + 16 * (i - 1))- Step (159, 15), RGB(100, 100, 120), BF
							c = IIf(dashitem(i).n <> 0, RGB(0, 250, 0), RGB(200, 50, 0))
							Draw String (760, 105 + 16 * (i - 1)), dashitem(i).iname, c
						Next i
					End If
				Case 2	'Steering wheel
					If dashzoom = 2 Then
						DrawSection 47, 90, 692, 502, "Dash view"
						Line (50, 100)- Step (639, 399), RGB(0, 0, 0), BF
						View Screen (50, 100)-(689, 499)
					Else
						View Screen (0, 0)-(959, 599)
					End If
					n = rbm("roof")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dash")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dasm")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, And
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, And
						End If
					End If
					n = rbm("dast")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, Or
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, Or
						End If
					End If
					n = rbm("whl2")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					For i As Byte = 0 To 30
						If dashzoom = 2 Then
							If i = steer_curspot Then
								Line (2 * par.steer(i).x + 49, 2 * par.steer(i).y + 99)- Step (4, 4), RGB(200, 100, 250), BF
								Line (2 * (2 * par.steer(0).x - par.steer(i).x) + 49, 2 * par.steer(i).y + 99)- Step (4, 4), RGB(200, 100, 250), BF
							Else
								Line (2 * par.steer(i).x + 50, 2 * par.steer(i).y + 100)- Step (2, 2), RGB(0, 250, 250), B
								Line (2 * (2 * par.steer(0).x - par.steer(i).x) + 50, 2 * par.steer(i).y + 100)- Step (2, 2), RGB(0, 250, 250), B
							End If
						Else
							If i = steer_curspot Then
								Line (3 * par.steer(i).x - 1, 3 * par.steer(i).y - 1)- Step (4, 4), RGB(200, 100, 250), BF
								Line (3 * (2 * par.steer(0).x - par.steer(i).x) - 1, 3 * par.steer(i).y - 1)- Step (4, 4), RGB(200, 100, 250), BF
							Else
								Line (3 * par.steer(i).x, 3 * par.steer(i).y)- Step (3, 3), RGB(0, 250, 250), B
								Line (3 * (2 * par.steer(0).x - par.steer(i).x), 3 * par.steer(i).y)- Step (2, 2), RGB(0, 250, 250), B
							End If
						End If
					Next i
					If arccontrol Then
						If dashzoom = 2 Then
							Circle (2 * steer_cx + 50, 2 * steer_cy + 100), 2 * steer_r, RGB(250, 250, 0), 0, 3.14159, steer_ex
						Else
							Circle (3 * steer_cx, 3 * steer_cy), 3 * steer_r, RGB(250, 250, 0), 0, 3.14159, steer_ex
						End If
					End If
					View Screen
					
					If dashzoom = 2 Then
						DrawSection 720, 90, 917, 232, "Steering wheel"
						Draw String (730, 100), "Spoke", GRAY_COL
						Draw String (815, 100), Str(steer_curspot), NUM_COL
						Draw String (730, 116), "Coors", GRAY_COL
						Draw String (815, 116), "(" & par.steer(steer_curspot).x & ", " & par.steer(steer_curspot).y & ")", NUM_COL
						Draw String (730, 148), "- ARC -", WHITE_COL
						Draw String (730, 164), "Spokes", GRAY_COL
						Draw String (815, 164), Str(steer_spots), NUM_COL
						Draw String (730, 180), "Radius", GRAY_COL
						Draw String (815, 180), Str(steer_r), NUM_COL
						Draw String (730, 196), "Coors", GRAY_COL
						Draw String (815, 196), "(" & steer_cx & ", " & steer_cy & ")", NUM_COL
						Draw String (730, 212), "Eccent.", GRAY_COL
						Draw String (815, 212), Left(Str(steer_ex), 5), NUM_COL
					End If
				Case 3	'Speed-o-meter
					If dashzoom = 2 Then
						DrawSection 47, 90, 692, 502, "Dash view"
						Line (50, 100)- Step (639, 399), RGB(0, 0, 0), BF
						View Screen (50, 100)-(689, 499)
					Else
						View Screen (0, 0)-(959, 599)
					End If
					n = rbm("roof")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dash")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dasm")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, And
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, And
						End If
					End If
					n = rbm("dast")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, Or
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, Or
						End If
					End If
					n = rbm("whl2")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("ins2")
					For i As Byte = 0 To par.spdnum
						If dashzoom = 3 Then
							If i = speed_curspot Then
								Line (3 * (par.spd(i).x + bitmap(n).x) - 1, 3 * (par.spd(i).y + bitmap(n).y) - 1)- Step (6, 6), RGB(200, 100, 250), BF
							Else
								Line (3 * (par.spd(i).x + bitmap(n).x), 3 * (par.spd(i).y + bitmap(n).y))- Step (3, 3), RGB(0, 250, 250), B
							End If
						Else
							If i = speed_curspot Then
								Line (2 * (par.spd(i).x + bitmap(n).x) + 49, 2 * (par.spd(i).y + bitmap(n).y) + 99)- Step (4, 4), RGB(200, 100, 250), BF
							Else
								Line (2 * (par.spd(i).x + bitmap(n).x) + 50, 2 * (par.spd(i).y + bitmap(n).y) + 100)- Step (2, 2), RGB(0, 250, 250), B
							End If
						End If
					Next i
					If dashzoom = 3 Then
						Line (3 * (par.spdneedle.x + bitmap(n).x) - 2, 3 * (par.spdneedle.y + bitmap(n).y) - 2)- Step (6, 6), RGB(0, 250, 100)
						Line (3 * (par.spdneedle.x + bitmap(n).x) - 2, 3 * (par.spdneedle.y + bitmap(n).y) + 4)- Step (6, -6), RGB(0, 250, 100)
						If speed_curspot = -1 Then Line (3 * (par.spdneedle.x + bitmap(n).x) - 2, 3 * (par.spdneedle.y + bitmap(n).y) - 2)- Step (6, 6), RGB(250, 250, 50), BF
						If arccontrol Then Circle (3 * speed_cx, 3 * speed_cy), 3 * speed_r, RGB(250, 250, 0), speed_start * speed_dir, -speed_end * speed_dir, speed_ex
					Else
						Line (2 * (par.spdneedle.x + bitmap(n).x) + 49, 2 * (par.spdneedle.y + bitmap(n).y) + 99)- Step (3, 3), RGB(0, 250, 100)
						Line (2 * (par.spdneedle.x + bitmap(n).x) + 49, 2 * (par.spdneedle.y + bitmap(n).y) + 102)- Step (3, -3), RGB(0, 250, 100)
						If speed_curspot = -1 Then Line (2 * (par.spdneedle.x + bitmap(n).x) + 49, 2 * (par.spdneedle.y + bitmap(n).y) + 99)- Step (3, 3), RGB(250, 250, 50), BF
						If arccontrol Then Circle (2 * speed_cx + 50, 2 * speed_cy + 100), 2 * speed_r, RGB(250, 250, 0), speed_start * speed_dir, -speed_end * speed_dir, speed_ex
					End If
					View Screen
					If dashzoom = 2 Then
						DrawSection 720, 90, 917, 312, "Speed-o-meter"
						
						If par.spdneedle.y = 65535 Then
							Draw String (730, 100), "Disabled", GRAY_COL
						Else
							Draw String (730, 100), "Spoke", GRAY_COL
							If speed_curspot = -1 Then
								Draw String (815, 100), "Needle ctr.", NUM_COL
								Draw String (730, 116), "Coors", GRAY_COL
								Draw String (815, 116), "(" & par.spdneedle.x & ", " & par.spdneedle.y & ")", NUM_COL
							Else
								Draw String (815, 100), speed_curspot & " / " & par.spdnum, NUM_COL
								Draw String (730, 116), "Coors", GRAY_COL
								Draw String (815, 116), "(" & par.spd(speed_curspot).x & ", " & par.spd(speed_curspot).y & ")", NUM_COL
							End If
							If speed_curspot >= 0 Then
								Draw String (730, 132), "Value", GRAY_COL
								Draw String (815, 132), Str(2.5 * speed_curspot) & " mph", NUM_COL
							End If
							If par.spdneedle.y = 0 Then
								Draw String (730, 148), "Digital", GRAY_COL
							Else
								Draw String (730, 148), "Max spd.", GRAY_COL
								Draw String (815, 148), 2.5 * par.spdnum & " mph", NUM_COL
							End If
						End If
						
						Draw String (730, 180), "- ARC -", WHITE_COL
						Draw String (730, 196), "Spokes", GRAY_COL
						Draw String (815, 196), Str(par.spdnum + 1), NUM_COL
						Draw String (730, 212), "Radius", GRAY_COL
						Draw String (815, 212), Str(speed_r), NUM_COL
						Draw String (730, 228), "Coors", GRAY_COL
						Draw String (815, 228), "(" & speed_cx & ", " & speed_cy & ")", NUM_COL
						Draw String (730, 244), "Eccent.", GRAY_COL
						Draw String (815, 244), Left(Str(speed_ex), 5), NUM_COL
						Draw String (730, 260), "Direct.", GRAY_COL
						Draw String (815, 260), IIf(speed_dir > 0, "CW", "CCW"), NUM_COL
						Draw String (730, 276), "Start", GRAY_COL
						Draw String (815, 2760), Left(Str(Abs(180 * speed_start / PI)), 5) & Chr(248), NUM_COL
						Draw String (730, 292), "End", GRAY_COL
						Draw String (815, 292), Left(Str(Abs(180 * speed_end / PI)), 5) & Chr(248), NUM_COL
					End If
				Case 4	'Tach-o-meter
					If dashzoom = 2 Then
						DrawSection 47, 90, 692, 502, "Dash view"
						Line (50, 100)- Step (639, 399), RGB(0, 0, 0), BF
						View Screen (50, 100)-(689, 499)
					Else
						View Screen (0, 0)-(959, 599)
					End If
					n = rbm("roof")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dash")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dasm")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, And
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, And
						End If
					End If
					n = rbm("dast")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, Or
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, Or
						End If
					End If
					n = rbm("whl2")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("ins2")
					For i As Byte = 0 To par.tacnum
						If dashzoom = 3 Then
							If i = tac_curspot Then
								Line (3 * (par.tac(i).x + bitmap(n).x) - 1, 3 * (par.tac(i).y + bitmap(n).y) - 1)- Step (6, 6), RGB(200, 100, 250), BF
							Else
								Line (3 * (par.tac(i).x + bitmap(n).x), 3 * (par.tac(i).y + bitmap(n).y))- Step (3, 3), RGB(0, 250, 250), B
							End If
						Else
							If i = tac_curspot Then
								Line (2 * (par.tac(i).x + bitmap(n).x) + 49, 2 * (par.tac(i).y + bitmap(n).y) + 99)- Step (4, 4), RGB(200, 100, 250), BF
							Else
								Line (2 * (par.tac(i).x + bitmap(n).x) + 50, 2 * (par.tac(i).y + bitmap(n).y) + 100)- Step (2, 2), RGB(0, 250, 250), B
							End If
						End If
					Next i
					If dashzoom = 3 Then
						Line (3 * (par.tacneedle.x + bitmap(n).x) - 2, 3 * (par.tacneedle.y + bitmap(n).y) - 2)- Step (6, 6), RGB(0, 250, 100)
						Line (3 * (par.tacneedle.x + bitmap(n).x) - 2, 3 * (par.tacneedle.y + bitmap(n).y) + 4)- Step (6, -6), RGB(0, 250, 100)
						If tac_curspot = -1 Then Line (3 * (par.tacneedle.x + bitmap(n).x) - 2, 3 * (par.tacneedle.y + bitmap(n).y) - 2)- Step (6, 6), RGB(250, 250, 50), BF
						If arccontrol Then Circle (3 * tac_cx, 3 * tac_cy), 3 * tac_r, RGB(250, 250, 0), tac_start * tac_dir, -tac_end * tac_dir, tac_ex
					Else
						Line (2 * (par.tacneedle.x + bitmap(n).x) + 49, 2 * (par.tacneedle.y + bitmap(n).y) + 99)- Step (3, 3), RGB(0, 250, 100)
						Line (2 * (par.tacneedle.x + bitmap(n).x) + 49, 2 * (par.tacneedle.y + bitmap(n).y) + 102)- Step (3, -3), RGB(0, 250, 100)
						If tac_curspot = -1 Then Line (2 * (par.tacneedle.x + bitmap(n).x) + 49, 2 * (par.tacneedle.y + bitmap(n).y) + 99)- Step (3, 3), RGB(250, 250, 50), BF
						If arccontrol Then Circle (2 * tac_cx + 50, 2 * tac_cy + 100), 2 * tac_r, RGB(250, 250, 0), tac_start * tac_dir, -tac_end * tac_dir, tac_ex
					End If
					View Screen
					If dashzoom = 2 Then
						DrawSection 720, 90, 917, 312, "Tach-o-meter"
						Draw String (730, 100), "Spoke", GRAY_COL
						If tac_curspot = -1 Then
							Draw String (815, 100), "Needle ctr.", NUM_COL
						Else
							Draw String (815, 100), tac_curspot & " / " & par.tacnum, NUM_COL
						End If
						Draw String (730, 116), "Coors", GRAY_COL
						Draw String (815, 116), "(" & par.tac(tac_curspot).x & ", " & par.tac(tac_curspot).y & ")", NUM_COL
						If tac_curspot >= 0 Then
							Draw String (730, 132), "Value", GRAY_COL
							Draw String (815, 132), Str(128 * tac_curspot) & " rpm", NUM_COL
						End If
						Draw String (730, 148), "Max RPM", GRAY_COL
						Draw String (815, 148), 128 * par.tacnum & " rpm", NUM_COL
						Draw String (730, 180), "- ARC -", WHITE_COL
						Draw String (730, 196), "Spokes", GRAY_COL
						Draw String (815, 196), Str(par.tacnum + 1), NUM_COL
						Draw String (730, 212), "Radius", GRAY_COL
						Draw String (815, 212), Str(tac_r), NUM_COL
						Draw String (730, 228), "Coors", GRAY_COL
						Draw String (815, 228), "(" & tac_cx & ", " & tac_cy & ")", NUM_COL
						Draw String (730, 244), "Eccent.", GRAY_COL
						Draw String (815, 244), Left(Str(tac_ex), 5), NUM_COL
						Draw String (730, 260), "Direct.", GRAY_COL
						Draw String (815, 260), IIf(tac_dir > 0, "CW", "CCW"), NUM_COL
						Draw String (730, 276), "Start", GRAY_COL
						Draw String (815, 276), Left(Str(Abs(180 * tac_start / PI)), 5) & Chr(248), NUM_COL
						Draw String (730, 292), "End", GRAY_COL
						Draw String (815, 292), Left(Str(Abs(180 * tac_end / PI)), 5) & Chr(248), NUM_COL
					End If
				Case 5	'Gear box
					If dashzoom = 2 Then
						DrawSection 47, 90, 692, 502, "Dash view"
						Line (50, 100)- Step (639, 399), RGB(0, 0, 0), BF
						View Screen (50, 100)-(689, 499)
					Else
						View Screen (0, 0)-(959, 599)
					End If
					n = rbm("roof")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dash")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("dasm")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, And
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, And
						End If
					End If
					n = rbm("dast")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, Or
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, Or
						End If
					End If
					n = rbm("whl2")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					n = rbm("gbox")
					If n Then
						If dashzoom = 3 Then
							Put(3 * bitmap(n).x, 3 * bitmap(n).y), bitmap(n).i3, PSet
						Else
							Put(2 * bitmap(n).x + 50, 2 * bitmap(n).y + 100), bitmap(n).i2, PSet
						End If
					End If
					For i As Byte = 1 To par.gears
						If dashzoom = 3 Then
							If curgear = i Then
								Line (3 * (bitmap(n).x + par.knob(i).x) - 1, 3 * (bitmap(n).y + par.knob(i).y) - 1)- Step (3, 3), RGB(250, 0, 200), B
							Else
								Line (3 * (bitmap(n).x + par.knob(i).x) - 1, 3 * (bitmap(n).y + par.knob(i).y) - 1)- Step (3, 3), RGB(0, 250, 250), B
							End If
							Draw String (3 * (bitmap(n).x + par.knob(i).x) - 2, 3 * (bitmap(n).y + par.knob(i).y) + 5), Str(i), RGB(0, 0, 0)
							Draw String (3 * (bitmap(n).x + par.knob(i).x) - 3, 3 * (bitmap(n).y + par.knob(i).y) + 4), Str(i), WHITE_COL
						Else
							If curgear = i Then
								Line (2 * (bitmap(n).x + par.knob(i).x) + 49, 2 * (bitmap(n).y + par.knob(i).y) + 99)- Step (2, 2), RGB(250, 0, 200), B
							Else
								Line (2 * (bitmap(n).x + par.knob(i).x) + 49, 2 * (bitmap(n).y + par.knob(i).y) + 99)- Step (2, 2), RGB(0, 250, 250), B
							End If
							Draw String (2 * (bitmap(n).x + par.knob(i).x) + 48, 2 * (bitmap(n).y + par.knob(i).y) + 105), Str(i), RGB(0, 0, 0)
							Draw String (2 * (bitmap(n).x + par.knob(i).x) + 47, 2 * (bitmap(n).y + par.knob(i).y) + 104), Str(i), WHITE_COL
						End If
					Next i
					If dashzoom = 3 Then
						If curgear = -1 Then
							Line (3 * (bitmap(n).x + par.knobh), 3 * bitmap(n).y)- Step (0, 3 * bitmap(n).h - 1), RGB(250, 0, 200)
							Line (3 * bitmap(n).x, 3 * (bitmap(n).y + par.knobv))- Step (3 * bitmap(n).w - 1, 0), RGB(250, 0, 200)
						Else
							Line (3 * (bitmap(n).x + par.knobh), 3 * bitmap(n).y)- Step (0, 3 * bitmap(n).h - 1), RGB(0, 250, 100)
							Line (3 * bitmap(n).x, 3 * (bitmap(n).y + par.knobv))- Step (3 * bitmap(n).w - 1, 0), RGB(0, 250, 100)
						End If					
					Else
						If curgear = -1 Then
							Line (2 * (bitmap(n).x + par.knobh) + 50, 2 * bitmap(n).y + 100)- Step (0, 2 * bitmap(n).h - 1), RGB(250, 0, 200)
							Line (2 * bitmap(n).x + 50, 2 * (bitmap(n).y + par.knobv) + 100)- Step (2 * bitmap(n).w - 1, 0), RGB(250, 0, 200)
						Else
							Line (2 * (bitmap(n).x + par.knobh) + 50, 2 * bitmap(n).y + 100)- Step (0, 2 * bitmap(n).h - 1), RGB(0, 250, 100)
							Line (2 * bitmap(n).x + 50, 2 * (bitmap(n).y + par.knobv) + 100)- Step (2 * bitmap(n).w - 1, 0), RGB(0, 250, 100)
						End If
					End If
					View Screen
					If dashzoom = 2 Then
						DrawSection 720, 90, 917, 296, "Gear box"
						Draw String (730, 100), "Editing", GRAY_COL
						If curgear > 0 Then
							Draw String (815, 100), "Gear " & curgear & "/" & par.gears, NUM_COL
						ElseIf curgear = -1 Then
							Draw String (815, 100), "Tabs", NUM_COL
						End If
					End If
			End Select
			ScreenUnlock
			
			update = 0
		End If
		
		akey = InKey
		GetMouse xm, ym, wm, bm
		
		If exxm <> xm OrElse exym <> ym OrElse exwm <> wm OrElse exbm <> bm Then
			Dim As Short viewx1 = 0, viewx2 = 960, viewy1 = 0, viewy2 = 600
			
			If dashzoom = 2 Then
				viewx1 = 50 : viewy1 = 100
				viewx2 = 690 : viewy2 = 500
			End If
			If bm = 1 Then
				If xm >= viewx1 AndAlso ym >= viewy1 AndAlso xm < viewx2 AndAlso ym < viewy2 Then
					Dim As Short vx, vy
					vx = (xm - viewx1) \ dashzoom
					vy = (ym - viewy1) \ dashzoom
					If opmode = 1 Then
						'Select image
						For i As Short = 1 To UBound(dashitem)
							If dashitem(i).n Then
								Dim nn As UByte
								
								nn = dashitem(i).n
								If vx >= bitmap(nn).rx AndAlso vx < bitmap(nn).rx + bitmap(nn).w _
									AndAlso vy >= bitmap(nn).ry AndAlso vy < bitmap(nn).ry + bitmap(nn).h Then
								
									curbm = i
									update = -1
									Exit For
								End If
							End If
						Next i
					ElseIf opmode = 2 Then
						'Select spoke from steering wheel
						Dim As Short thisdist, distsofar
						Dim As Byte thisspoke = -1
						
						distsofar = 8
						For i As Byte = 0 To 30
							thisdist = Abs(vx - par.steer(i).x) + Abs(vy - par.steer(i).y)
							If thisdist < distsofar Then
								thisspoke = i
								distsofar = thisdist
							End If
							thisdist = Abs(vx - (2 * par.steer(0).x - par.steer(i).x)) + Abs(vy - par.steer(i).y)
							If thisdist < distsofar Then
								thisspoke = i
								distsofar = thisdist
							End If
						Next i
						
						If thisspoke >= 0 Then
							steer_curspot = thisspoke
							update = -1
						End If
					ElseIf opmode = 3 Then
						'Select speed-o-meter point
						Dim As Short thisdist, distsofar
						Dim As Byte thisspot = -2
						
						n = rbm("ins2")
						distsofar = 8
						For i As Byte = 0 To par.spdnum
							thisdist = Abs(vx - (par.spd(i).x + bitmap(n).x)) + Abs(vy - (par.spd(i).y + bitmap(n).y))
							If thisdist < distsofar Then
								thisspot = i
								distsofar = thisdist
							End If
						Next i
						thisdist = Abs(vx - (par.spdneedle.x + bitmap(n).x)) + Abs(vy - (par.spdneedle.y + bitmap(n).y))
						If thisdist < distsofar Then
							thisspot = -1
							distsofar = thisdist
						End If
						
						If thisspot >= -1 Then
							speed_curspot = thisspot
							update = -1
						End If
					ElseIf opmode = 4 Then
						'Select tach-o-meter point
						Dim As Short thisdist, distsofar
						Dim As Byte thisspot = -2
						
						n = rbm("ins2")
						distsofar = 8
						For i As Byte = 0 To par.tacnum
							thisdist = Abs(vx - (par.tac(i).x + bitmap(n).x)) + Abs(vy - (par.tac(i).y + bitmap(n).y))
							If thisdist < distsofar Then
								thisspot = i
								distsofar = thisdist
							End If
						Next i
						thisdist = Abs(vx - (par.tacneedle.x + bitmap(n).x)) + Abs(vy - (par.tacneedle.y + bitmap(n).y))
						If thisdist < distsofar Then
							thisspot = -1
							distsofar = thisdist
						End If
						
						If thisspot >= -1 Then
							tac_curspot = thisspot
							update = -1
						End If
					ElseIf opmode = 5 Then
						'Select gear
						Dim As Short thisdist, distsofar
						Dim As Byte thisspot = 0
						
						n = rbm("gbox")
						distsofar = 8
						For i As Byte = 1 To par.gears
							thisdist = Abs(vx - (par.knob(i).x + bitmap(n).x)) + Abs(vy - (par.knob(i).y + bitmap(n).y))
							If thisdist < distsofar Then
								thisspot = i
								distsofar = thisdist
							End If
						Next i
						thisdist = Abs(vx - (par.knobh + bitmap(n).x)) + Abs(vy - (par.knobv + bitmap(n).y))
						If thisdist < distsofar Then
							thisspot = -1
							distsofar = thisdist
						End If
						
						If thisspot <> 0 Then
							curgear = thisspot
							update = -1
						End If
					End If
				End If
			ElseIf bm = 2 Then
				If xm >= viewx1 AndAlso ym >= viewy1 AndAlso xm < viewx2 AndAlso ym < viewy2 Then
					Dim As Short vx, vy
					vx = (xm - viewx1) \ dashzoom
					vy = (ym - viewy1) \ dashzoom
					
					If opmode = 1 Then
						'Move image
						If dashitem(curbm).n Then
							n = dashitem(curbm).n
							MoveDashImageTo n, (xm - viewx1 - bitmap(n).w) \ 2, (ym - viewy1 - bitmap(n).h) \ 2
							update = -1
						End If
					ElseIf opmode = 2 Then
						'Move steering wheel spoke
						If steer_curspot = 0 Then
							par.steer(0).x = vx
							par.steer(0).y = vy
						ElseIf vx >= par.steer(0).x Then
							par.steer(steer_curspot).x = vx
							par.steer(steer_curspot).y = vy
						Else
							par.steer(steer_curspot).x = 2 * par.steer(0).x - vx
							par.steer(steer_curspot).y = vy
						End If
						update = -1
					ElseIf opmode = 3 Then
						'Move speed-o-meter spot
						n = rbm("ins2")
						If speed_curspot = -1 Then
							par.spdneedle.x = vx - bitmap(n).x
							par.spdneedle.y = vy - bitmap(n).y
						Else
							par.spd(speed_curspot).x = vx - bitmap(n).x
							par.spd(speed_curspot).y = vy - bitmap(n).y
						End If
						update = -1
					ElseIf opmode = 4 Then
						'Move tach-o-meter spot
						n = rbm("ins2")
						If tac_curspot = -1 Then
							par.tacneedle.x = vx - bitmap(n).x
							par.tacneedle.y = vy - bitmap(n).y
						Else
							par.tac(tac_curspot).x = vx - bitmap(n).x
							par.tac(tac_curspot).y = vy - bitmap(n).y
						End If
						update = -1
					ElseIf opmode = 5 Then
						'Move gear position
						n = rbm("gbox")
						If curgear = -1 Then
							par.knobh = vx - bitmap(n).x
							par.knobv = vy - bitmap(n).y
						Else
							par.knob(curgear).x = vx - bitmap(n).x
							par.knob(curgear).y = vy - bitmap(n).y
						End If
						update = -1
					End If
				End If
			End If
			exxm = xm : exym = ym : exwm = wm : exbm = bm
		End If
		
		If MaintainMenu < 0 Then Exit Do
		
		Select Case akey
			Case Chr(9)	'TAB - Change OpMode
				If MultiKey(&H1D) Then
					'Ctrl+Tab for GNU/Linux
					opmode = (opmode + 5) Mod 6 : update = -1
				Else
					opmode = (opmode + 1) Mod 6 : update = -1
				End If
			Case Chr(255, 15)	'Shift+Tab
				opmode = (opmode + 5) Mod 6 : update = -1
			Case Chr(255, 73)	'PgUp
				If opmode = 1 Then
					If curbm = 1 Then
						curbm = UBound(dashitem)
					Else
						curbm -= 1
					End If					
					update = -1
				ElseIf opmode = 2 Then	'Steering wheel
					If arccontrol Then
						If steer_r < 200 Then
							steer_r += 1
							update = -1
						End If
					Else
						If steer_curspot < 30 Then
							steer_curspot += 1
							update = -1
						End If
					End If
				ElseIf opmode = 3 Then	'Speed-o-meter
					If arccontrol Then
						If speed_r < 150 Then
							speed_r += 1
							update = -1
						End If
					Else
						If speed_curspot < par.spdnum Then
							speed_curspot += 1
							update = -1
						End If
					End If
				ElseIf opmode = 4 Then	'Tach-o-meter
					If arccontrol Then
						If tac_r < 150 Then
							tac_r += 1
							update = -1
						End If
					Else
						If tac_curspot < par.tacnum Then
							tac_curspot += 1
							update = -1
						End If
					End If
				ElseIf opmode = 5 Then	'Gear box
					curgear += 1
					If curgear = 0 Then
						curgear = 1
					ElseIf curgear > par.gears Then
						curgear = -1
					End If
					update = -1
				End If
			Case Chr(255, 81)	'PgDn
				If opmode = 1 Then
					If curbm = UBound(dashitem) Then
						curbm = 1
					Else
						curbm += 1
					End If
					update = -1
				ElseIf opmode = 2 Then	'Steering wheel
					If arccontrol Then
						If steer_r > 10 Then
							steer_r -= 1
							update = -1
						End If
					Else
						If steer_curspot > 0 Then
							steer_curspot -= 1
							update = -1
						End If
					End If
				ElseIf opmode = 3 Then	'Speed-o-meter
					If arccontrol Then
						If speed_r > 8 Then
							speed_r -= 1
							update = -1
						End If
					Else
						If speed_curspot >= 0 Then
							speed_curspot -= 1
							update = -1
						End If
					End If
				ElseIf opmode = 4 Then	'Tach-o-meter
					If arccontrol Then
						If tac_r > 8 Then
							tac_r -= 1
							update = -1
						End If
					Else
						If tac_curspot >= 0 Then
							tac_curspot -= 1
							update = -1
						End If
					End If
				ElseIf opmode = 5 Then	'Gear box
					curgear -= 1
					If curgear = 0 Then
						curgear = -1
					ElseIf curgear = -2 Then
						curgear = par.gears
						If curgear = 0 Then curgear = 1
					End If
					update = -1
				End If
			Case Chr(255, 72)	'Up
				If opmode = 1 Then	'Move image
					n = dashitem(curbm).n
					If n Then
						MoveDashImageTo n, bitmap(n).rx, bitmap(n).ry - 1
						update = -1
					End If
				Elseif opmode = 2 Then
					If arccontrol Then
						If steer_cy > 50 Then
							steer_cy -= 1
							update = -1
						End If
					Else
						If par.steer(steer_curspot).y > 50 Then
							par.steer(steer_curspot).y -= 1
							update = -1
						End If
					End If
				Elseif opmode = 3 Then	'Speed-o-meter
					If arccontrol Then
						If speed_cy > 0 Then
							speed_cy -= 1
							update = -1
						End If
					Else
						If speed_curspot = -1 Then
							If par.spdneedle.y > 0 Then
								par.spdneedle.y -= 1
								update = -1
							End If
						ElseIf par.spd(speed_curspot).y > 0 Then
							par.spd(speed_curspot).y -= 1
							update = -1
						End If
					End If
				Elseif opmode = 4 Then	'Tach-o-meter
					If arccontrol Then
						If tac_cy > 0 Then
							tac_cy -= 1
							update = -1
						End If
					Else
						If tac_curspot = -1 Then
							If par.tacneedle.y > 0 Then
								par.tacneedle.y -= 1
								update = -1
							End If
						ElseIf par.tac(tac_curspot).y > 0 Then
							par.tac(tac_curspot).y -= 1
							update = -1
						End If
					End If
				ElseIf opmode = 5 Then	'Gear box
					If curgear > 0 Then
						If par.knob(curgear).y Then par.knob(curgear).y -= 1
					ElseIf curgear = -1 Then
						If par.knobv Then par.knobv -= 1
					End If
					update = -1
				End If
			Case Chr(255, 80)	'Down
				If opmode = 1 Then	'Move image
					n = dashitem(curbm).n
					If n Then
						MoveDashImageTo n, bitmap(n).rx, bitmap(n).ry + 1
						update = -1
					End If
				Elseif opmode = 2 Then
					If arccontrol Then
						If steer_cy < 199 Then
							steer_cy += 1
							update = -1
						End If
					Else
						If par.steer(steer_curspot).y < 199 Then
							par.steer(steer_curspot).y += 1
							update = -1
						End If
					End If
				Elseif opmode = 3 Then	'Speed-o-meter
					If arccontrol Then
						If speed_cy < 199 Then
							speed_cy += 1
							update = -1
						End If
					Else
						If speed_curspot = -1 Then
							If par.spdneedle.y < 199 Then
								par.spdneedle.y += 1
								update = -1
							End If
						ElseIf par.spd(speed_curspot).y < 199 Then
							par.spd(speed_curspot).y += 1
							update = -1
						End If
					End If
				Elseif opmode = 4 Then	'Tach-o-meter
					If arccontrol Then
						If tac_cy < 199 Then
							tac_cy += 1
							update = -1
						End If
					Else
						If tac_curspot = -1 Then
							If par.tacneedle.y < 199 Then
								par.tacneedle.y += 1
								update = -1
							End If
						ElseIf par.tac(tac_curspot).y < 199 Then
							par.tac(tac_curspot).y += 1
							update = -1
						End If
					End If
				ElseIf opmode = 5 Then	'Gear box
					n = rbm("gbox")
					If curgear > 0 Then
						If par.knob(curgear).y < bitmap(n).h - 1 Then par.knob(curgear).y += 1
					ElseIf curgear = -1 Then
						If par.knobv < bitmap(n).h - 1 Then par.knobv += 1
					End If
					update = -1
				End If
			Case Chr(255, 75)	'Left
				If opmode = 1 Then	'Move image
					n = dashitem(curbm).n
					If n Then
						MoveDashImageTo n, bitmap(n).rx - 1, bitmap(n).ry
						update = -1
					End If
				Elseif opmode = 2 Then
					If arccontrol Then
						If steer_cx > 0 Then
							steer_cx -= 1
							update = -1
						End If
					Else
						If par.steer(steer_curspot).x > 0 Then
							par.steer(steer_curspot).x -= 1
							update = -1
						End If
					End If
				Elseif opmode = 3 Then	'Speed-o-meter
					If arccontrol Then
						If speed_cx > 0 Then
							speed_cx -= 1
							update = -1
						End If
					Else
						If speed_curspot = -1 Then
							If par.spdneedle.x > 0 Then
								par.spdneedle.x -= 1
								update = -1
							End If
						ElseIf par.spd(speed_curspot).x > 0 Then
							par.spd(speed_curspot).x -= 1
							update = -1
						End If
					End If
				Elseif opmode = 4 Then	'Tach-o-meter
					If arccontrol Then
						If tac_cx > 0 Then
							tac_cx -= 1
							update = -1
						End If
					Else
						If tac_curspot = -1 Then
							If par.tacneedle.x > 0 Then
								par.tacneedle.x -= 1
								update = -1
							End If
						ElseIf par.tac(tac_curspot).x > 0 Then
							par.tac(tac_curspot).x -= 1
							update = -1
						End If
					End If
				ElseIf opmode = 5 Then	'Gear box
					If curgear > 0 Then
						If par.knob(curgear).x Then par.knob(curgear).x -= 1
					ElseIf curgear = -1 Then
						If par.knobh Then par.knobh -= 1
					End If
					update = -1
				End If
			Case Chr(255, 77)	'Right
				If opmode = 1 Then	'Move image
					n = dashitem(curbm).n
					If n Then
						MoveDashImageTo n, bitmap(n).rx + 1, bitmap(n).ry
						update = -1
					End If
				Elseif opmode = 2 Then
					If arccontrol Then
						If steer_cx < 255 Then
							steer_cx += 1
							update = -1
						End If
					Else
						If par.steer(steer_curspot).x < 255 Then
							par.steer(steer_curspot).x += 1
							update = -1
						End If
					End If
				Elseif opmode = 3 Then	'Speed-o-meter
					If arccontrol Then
						If speed_cx < 255 Then
							speed_cx += 1
							update = -1
						End If
					Else
						If speed_curspot = -1 Then
							If par.spdneedle.x < 255 Then
								par.spdneedle.x += 1
								update = -1
							End If
						ElseIf par.spd(speed_curspot).x < 255 Then
							par.spd(speed_curspot).x += 1
							update = -1
						End If
					End If
				ElseIf opmode = 4 Then	'Tach-o-meter
					If arccontrol Then
						If tac_cx < 255 Then
							tac_cx += 1
							update = -1
						End If
					Else
						If tac_curspot = -1 Then
							If par.tacneedle.x < 255 Then
								par.tacneedle.x += 1
								update = -1
							End If
						ElseIf par.tac(tac_curspot).x < 255 Then
							par.tac(tac_curspot).x += 1
							update = -1
						End If
					End If
				ElseIf opmode = 5 Then	'Gear box
					n = rbm("gbox")
					If curgear > 0 Then
						If par.knob(curgear).x < bitmap(n).w - 1 Then par.knob(curgear).x += 1
					ElseIf curgear = -1 Then
						If par.knobh < bitmap(n).w - 1 Then par.knobh += 1
					End If
					update = -1
				End If
			Case Chr(13)		'Enter
				If opmode = 1 Then	'Load image
					Dim s As String
					
					'Color RGB(250, 250, 250)
					'Locate 30, 96
					'Line Input s
					s = FileHandling(2)
					If Left(s, 2) = "L:" Then
						LoadBitmap dashitem(curbm).id, Mid(s, 3)
					ElseIf Left(s, 2) = "S:" Then
						SaveBitmap dashitem(curbm).id, Mid(s, 3)
					End If
					update = -1
				ElseIf opmode = 2 Then	'Steering wheel
					Dim sp(0 To 30) As IntCoor2D
					Dim c As IntCoor2D
					
					c.x = steer_cx
					c.y = steer_cy					
					GenerateSpokes sp(), 30, c, steer_r, PI / 2, 0, steer_ex, 1
					For i As Byte = 0 To 30
						par.steer(i).x = sp(i).x
						par.steer(i).y = sp(i).y
					Next i
					update = -1
				ElseIf opmode = 3 Then	'Speed-o-meter
					Dim sp(0 To par.spdnum) As IntCoor2D
					Dim c As IntCoor2D, st As Double, en As Double
					
					n = rbm("ins2")
					c.x = speed_cx : par.spdneedle.x = speed_cx - bitmap(n).x
					c.y = speed_cy : par.spdneedle.y = speed_cy - bitmap(n).y
					en = speed_start : st = speed_end
					If st < en Then st += 2 * PI
					
					GenerateSpokes sp(), par.spdnum, c, speed_r, st, en, speed_ex, speed_dir
					For i As Byte = 0 To par.spdnum
						par.spd(i).x = sp(i).x - bitmap(n).x
						par.spd(i).y = sp(i).y - bitmap(n).y
					Next i
					For i As Byte = par.spdnum + 1 To 103
						par.spd(i) = par.spd(par.spdnum)
					Next i
					update = -1
				ElseIf opmode = 4 Then	'Tach-o-meter
					Dim sp(0 To par.tacnum) As IntCoor2D
					Dim c As IntCoor2D, st As Double, en As Double
					
					n = rbm("ins2")
					c.x = tac_cx : par.tacneedle.x = tac_cx - bitmap(n).x
					c.y = tac_cy : par.tacneedle.y = tac_cy - bitmap(n).y
					en = tac_start : st = tac_end
					If st < en Then st += 2 * PI
					
					GenerateSpokes sp(), par.tacnum, c, tac_r, st, en, tac_ex, tac_dir
					For i As Short = 0 To par.tacnum
						par.tac(i).x = sp(i).x - bitmap(n).x
						par.tac(i).y = sp(i).y - bitmap(n).y
					Next i
					For i As Short = par.tacnum + 1 To 127
						par.tac(i) = par.tac(par.tacnum)
					Next i
					update = -1
				End If
			Case Chr(255, 83)	'Delete
				If opmode = 1 Then
					n = dashitem(curbm).n
					If n Then
						RemoveBitmap n
						update = -1
					End If
				End If
			Case Chr(19)	'Ctrl+S
				If opmode = 1 Then	'Save image
					Dim s As String
					
					'Color RGB(250, 50, 0)
					'Locate 30, 96
					'Line Input s
					s = FileHandling(2)
					If Left(s, 2) = "S:" Then
						SaveBitmap dashitem(curbm).id, Mid(s, 3)
					End If
				End If
			Case Chr(4)		'Ctrl+D (digital/no speed-o-meter)
				If opmode = 3 Then
					If par.spdneedle.x = 0 AndAlso par.spdneedle.y = 0 Then
						'If currently a digital speed-o-meter, disable
						par.spdneedle.x = -1 : par.spdneedle.y = -1
						par.spdnum = 50
						speed_curspot = 0
						For i As Short = 0 To 50
							par.spd(i).x = 0 : par.spd(i).y = 0
						Next i
					Else
						'Otherwise, create a digital speed-o-meter
						par.spdneedle.x = 0 : par.spdneedle.y = 0
						par.spdnum = 3
						speed_curspot = 0
						For i As Short = 0 To 50
							par.spd(i).x = 0 : par.spd(i).y = 0
						Next i
						par.spd(0).x = 8: par.spd(1).x = 16 : par.spd(2).x = 24
					End If
					update = -1
				End If
			Case " "
				arccontrol = Not arccontrol
				update = -1
			Case ",", "<"
				If opmode = 2 Then	'Steer
					If arccontrol Then
						If steer_ex > .5 Then
							steer_ex /= 1.05
							update = -1
						End If
					End If
				ElseIf opmode = 3 Then	'Speed-o-meter
					If arccontrol Then
						If speed_ex > .5 Then
							speed_ex /= 1.05
							update = -1
						End If
					End If
				ElseIf opmode = 4 Then	'Tach-o-meter
					If arccontrol Then
						If tac_ex > .5 Then
							tac_ex /= 1.05
							update = -1
						End If
					End If
				End If
			Case ".", ">"
				If opmode = 2 Then	'Steer
					If arccontrol Then
						If steer_ex < 2 Then
							steer_ex *= 1.05
							update = -1
						End If
					End If
				ElseIf opmode = 3 Then	'Speed-o-meter
					If arccontrol Then
						If speed_ex < 2 Then
							speed_ex *= 1.05
							update = -1
						End If
					End If
				ElseIf opmode = 4 Then	'Tach-o-meter
					If arccontrol Then
						If tac_ex < 2 Then
							tac_ex *= 1.05
							update = -1
						End If
					End If
				End If
			Case "d", "D"	'Change gauge direction
				If arccontrol Then
					If opmode = 3 Then
						speed_dir = -speed_dir
						update = -1
					ElseIf opmode = 4 Then
						tac_dir = -tac_dir
						update = -1
					End If
				End If
			Case "o", "O"	'Change gauge orientation
				If arccontrol Then
					If opmode = 3 Then
						Swap speed_start, speed_end
						update = -1
					ElseIf opmode = 4 Then
						Swap tac_start, tac_end
						update = -1
					End If
				End If
			Case "+", "="
				If opmode = 2 Then	'Steering wheel
					If steer_spots < 30 Then
						steer_spots += 1
						update = -1
					End If
				ElseIf opmode = 3 Then	'Speed-o-meter
					If arccontrol Then
						If Sgn(speed_end - speed_start) * speed_dir > 0 Then
							speed_start += .05
							If speed_start > 2 * PI Then speed_start -= 2 * PI
						Else
							speed_end += .05
							If speed_end > 2 * PI Then speed_end -= 2 * PI
						End If
						update = -1
					Else
						If par.spdnum < 103 Then
							par.spdnum += 1
							update = -1
						End If
					End If
				ElseIf opmode = 4 Then	'Tach-o-meter
					If arccontrol Then
						If Sgn(tac_end - tac_start) * tac_dir > 0 Then
							tac_start += .05
							If tac_start > 2 * PI Then tac_start -= 2 * PI
						Else
							tac_end += .05
							If tac_end > 2 * PI Then tac_end -= 2 * PI
						End If
						update = -1
					Else
						If par.tacnum < 127 Then
							par.tacnum += 1
							update = -1
						End If
					End If
				ElseIf opmode = 5 Then	'Gear box
					If par.gears < 6 Then
						par.gears += 1
						update = -1
					End If
				End If
			CAse "-"
				If opmode = 2 Then	'Steering wheel
					If steer_spots > 1 Then
						steer_spots -= 1
						update = -1
					End If
				ElseIf opmode = 3 Then	'Speed-o-meter
					If arccontrol Then
						If Sgn(speed_end - speed_start) * speed_dir > 0 Then
							speed_start -= .05
							If speed_start < 0 Then speed_start += 2 * PI
						Else
							speed_end -= .05
							If speed_end < 0 Then speed_end += 2 * PI
						End If
						update = -1
					Else
						If par.spdnum > 0 Then
							par.spdnum -= 1
							update = -1
						End If
					End If
				ElseIf opmode = 4 Then	'Tach-o-meter
					If arccontrol Then
						If Sgn(tac_end - tac_start) * tac_dir > 0 Then
							tac_start -= .05
							If tac_start < 0 Then tac_start += 2 * PI
						Else
							tac_end -= .05
							If tac_end < 0 Then tac_end += 2 * PI
						End If
						update = -1
					Else
						If par.tacnum > 0 Then
							par.tacnum -= 1
							update = -1
						End If
					End If
				ElseIf opmode = 5 Then	'Gear box
					If par.gears > 0 Then
						par.gears -= 1
						If curgear > par.gears Then curgear = par.gears
						If curgear = 0 Then curgear = -1
						update = -1
					End If
				End If
			Case "0" To "9"
				If opmode = 1 Then
					dashitem(15).id = "dig" & akey
					Mid(dashitem(15).iname, Len(dashitem(15).iname), 1) = akey
					UpdateDashItems
					update = -1
				End If
			Case "q", "Q"	'Dash zoom
				dashzoom = 5 - dashzoom
				update = -1
			Case Chr(27), Chr(255) & "k"
				curmenu = -1
				Exit Do
		End Select
		
		If opmode = 0 Then	'Drive mode controls
			steerd = 0
			If MultiKey(77) Then steerd = 1
			If MultiKey(75) Then steerd = -1
			If steerd <> exsteerd Then
				update = -1
				exsteerd = steerd
			End If
		End If
	Loop
	
	Line (0, 0)-(959, 599), B_COL, BF
End Sub


Function Config (id As String) As String
	For i As Short = 1 To configs
		If LCase(config_id(i)) = LCase(id) Then
			Return config_value(i)
		End If
	Next i
	
	Return ""
End Function


Sub LoadConfig
	Dim f As Integer, s As String, n As Short
	Dim confdir(1 To 5) As String, i As Short
	
	'Set program directory
	progdir = ExePath
	If Right(progdir, 1) <> SEP Then progdir &= SEP
		
	confdir(1) = Environ("HOME") & SEP & "carworks" & SEP
	confdir(2) = Environ("APPDATA") & SEP & "carworks" & SEP
	confdir(3) = Environ("HOME") & SEP & ".carworks" & SEP
	confdir(4) = CurDir & SEP
	s = Command(0)
	n = InStrRev(s, SEP)
	If n Then s = Left(s, n) Else s = "." & SEP
	confdir(5) = s
	For i = 1 To UBound(confdir)
		If FileExists(confdir(i) & "carworks.cfg") Then
			workdir = confdir(i)
			Exit For
		End If
	Next i
		
	If i > UBound(confdir) Then
		workdir = CurDir & SEP
		Exit Sub
	End If
		
	f = FreeFile
	Open workdir & "carworks.cfg" For Input As f
	
	Do While Not EoF(f)
		Line Input #f, s
		s = Trim(s)
		
		If Len(s) <> 0 AndAlso Left(s, 1) <> ";" AndAlso Left(s, 1) <> "#" Then
			n = InStr(s, "=")
			If n Then
				configs += 1
				If configs > UBound(config_id) Then Exit Do
				config_id(configs) = RTrim(Left(s, n - 1))
				config_value(configs) = LTrim(Mid(s, n + 1))
			End If
		End If
	Loop
	
	Close f
End Sub


Sub UpdateFileList (exts As String = "")
	Dim s As String, n As Short, q As Byte
	Dim t As String, attr As Integer, f As Integer
	
	lfiles = 0
	
	'We should first look for dirlinks
	For i As Short = 1 To configs
		If LCase(config_id(i)) = "dirlink" Then
			n = InStr(config_value(i), ":")
			If n Then
				lfiles += 1
				lfile(lfiles).name = Trim(Mid(config_value(i), n + 1))
				lfile(lfiles).title = Trim(Left(config_value(i), n - 1))
				lfile(lfiles).type = 3
			Else
				lfiles += 1
				lfile(lfiles).name = Trim(config_value(i))
				lfile(lfiles).title = lfile(lfiles).name
				lfile(lfiles).type = 3
			End If
		End If
	Next i
	
	'Drives
	#ifndef __FB_LINUX__
		f = FreeFile
		For i As Byte = 3 To 26
			Open Chr(64 + i) + ":\NUL" For Input As f
			If Err = 0 Then
				lfiles += 1
				lfile(lfiles).name = Chr(64 + i) + ":"
				lfile(lfiles).title = lfile(lfiles).name
				lfile(lfiles).type = 2
				Close f
			End If
		Next i
	#endif
	
	#ifdef __FB_LINUX__
		s = Dir("*", -1, attr)
	#else
		s = Dir("*.*", -1, attr)
	#endif
	Do While Len(s)
		If attr And 16 Then		'Directory
			If s <> "." Then
				lfiles += 1
				lfile(lfiles).name = s
				lfile(lfiles).title = s
				lfile(lfiles).type = 1
			End If
		Else					'File
			If exts = "cars" Then	'Only complete cars
				If Right(LCase(s), 4) = ".res" Then
					lfiles += 1
					lfile(lfiles).name = LCase(Mid(s, 4, 4))
					lfile(lfiles).type = 0
					
					'Read car name from file
					Dim ff As Short = FreeFile, pgnam As Long
					Dim chunks As Short, cids As String
					Open s For Binary Access Read As ff
					Get #ff, 5, chunks
					cids = Space(4 * chunks)
					Get #ff, , cids
					Get #ff, InStr(cids, "gnam") + 6 + 4 * chunks, pgnam
					pgnam += 7 + 8 * chunks
					cids = Space(40)
					Get #ff, pgnam, cids
					If InStr(cids, Chr(0)) Then cids = Left(cids, InStr(cids, Chr(0)) - 1)
					Close ff
					lfile(lfiles).title = cids & " (" & UCase(Mid(s, 4, 4)) & ")"
				ElseIf Right(LCase(s), 4) = ".car" Then
					lfiles += 1
					lfile(lfiles).name = s
					lfile(lfiles).title = s	'For now
					lfile(lfiles).type = 0
					
					'Read car name from file
					Dim ff As Short = FreeFile
					Dim fp As Long, stri As String, l As Short
					Open s For Binary Access Read As ff
					stri = Space(80)
					Get #ff, , stri
					Get #ff, InStr(stri, "Base") + 4, fp
					Get #ff, fp + 1, l
					stri = Space(l)
					Get #ff, , stri
					Close ff
					lfile(lfiles).title = stri & " (project)"
				End If
			ElseIf Len(exts) Then	'Only specified extensions
				n = InStrRev(s, ".")
				If n Then
					t = LCase(Mid(s, n)) & "."
					If InStr(LCase(exts), t) Then
						q = -1
					Else
						q = 0
					End If
				Else
					q = 0
				End If
			Else
				q = -1
			End If
		
			If q Then
				lfiles += 1
				lfile(lfiles).name = s
				lfile(lfiles).title = s
				lfile(lfiles).type = 0
			End If
		End If
		
		s = Dir(attr)
	Loop
	
	'Sort files
	Do
		q = 0
		For i As Short = 1 To lfiles - 1
			If lfile(i).type = lfile(i + 1).type Then
				If LCase(lfile(i).title) > LCase(lfile(i + 1).title) Then
					Swap lfile(i), lfile(i + 1)
					q = -1
				End If
			ElseIf lfile(i).type < lfile(i + 1).type Then
				Swap lfile(i), lfile(i + 1)
				q = -1
			End If
		Next i
	Loop Until q = 0
End Sub


Function FileHandling (ftype As Byte = 1) As String
	Dim akey As String, s As String, n As Short
	Dim As Integer xm, ym, wm, bm
	Dim As Integer exxm, exym, exwm, exbm
	Dim exts As String, pointed As Short = 0, expointed As Short = 0
	Dim readfiles As Byte = -1, firstfile As Short = 1
	Dim redrawlist As Byte = -1, redrawedits As Byte = -1
	Dim shootfile As Short, outfile As String
	Dim editing As Byte = 2, editpos As Short = 0
	Dim edline(1 To 2) As String, barecarexts As String
	
	barecarexts = ".res.vsh.3sh.obj.car."
	If Len(Config("stunpack")) Then barecarexts &= "p3s.pvs.pre."
	
	Select Case ftype
		Case 1		'Car files
			'This one should be toggled to barecarexts
			'to import/export individual files
			exts = "cars"
		Case 2		'Image files
			#ifdef __USE_SOIL__
				exts = ".tga.bmp.jpg.jpeg.png.gif.pcx."
			#else
				exts = ".tga.bmp."
				If Len(Config("imagick")) Then exts &= "png.jpg.jpeg.gif.pcx."
			#endif
		Case Else	'All files
			exts = ""
	End Select
	
	Line (0, 0)-(959, 599), B_COL, BF
	Draw String (16, 16), "= FILES =", WHITE_COL

	DrawSection 16, 50, 599, 99, "Input"
	DrawSection 16, 119, 599, 589, "Files"
	DrawSection 620, 50, 939, 199, "Options"
	
	buttons = 0
	CreateSimpleButton 650, 70, " Load File ", 1
	AppendButton " Save File ", 2
	If ftype = 1 Then
		CreateSimpleButton 650, 110, " Toggle Cars/Files ", 3
	End If
	
	edline(1) = CurDir : edline(2) = ""

	Do
		If readfiles Then
			UpdateFileList exts
			firstfile = 1
			readfiles = 0
			redrawlist = -1
		End If
		If redrawedits Then
			ScreenLock
			Line (90, 60)-(597, 91), B_COL, BF
			Draw String (30, 60), "Dir:", GRAY_COL
			Draw String (90, 60), edline(1), NUM_COL
			If editing = 1 Then Line (8 * editpos + 90, 60)- Step (1, 15), RGB(0, 250, 0), B
			Draw String (30, 76), "File:", GRAY_COL
			Draw String (90, 76), edline(2), NUM_COL
			If editing = 2 Then Line (8 * editpos + 90, 76)- Step (1, 15), RGB(0, 250, 0), B
			ScreenUnlock
			redrawedits = 0
		End If
		If redrawlist Then
			ScreenLock
			Line (18, 130)-(597, 585), B_COL, BF
			For i As Short = 0 To 27
				If i + firstfile > lfiles Then Exit For
				Select Case lfile(i + firstfile).type
					Case 0
						If pointed = i Then
							Color RGB(240, 240, 240)
						Else
							Color RGB(180, 180, 180)
						End If
						s = lfile(i + firstfile).title
					Case 1
						If pointed = i Then
							Color RGB(200, 250, 200)
						Else
							Color RGB(0, 250, 0)
						End If
						s = "[" & lfile(i + firstfile).title & "]"
					Case 2, 3
						If pointed = i Then
							Color RGB(200, 250, 200)
						Else
							Color RGB(0, 250, 0)
						End If
						s = "[ - " & lfile(i + firstfile).title & " - ]"
				End Select
				Draw String (50, 130 + 16 * i), s
			Next i
			ScreenUnlock
			redrawlist = 0
		End If
		
		akey = InKey
		GetMouse xm, ym, wm, bm
		
		If xm <> exxm OrElse ym <> exym OrElse bm <> exbm OrElse wm <> exwm Then
			If xm >= 40 AndAlso xm <= 549 AndAlso ym >= 130 AndAlso ym <= 579 Then
				pointed = (ym - 130) \ 16
				If pointed <> expointed Then
					expointed = pointed
					redrawlist = -1
				End If
				If bm = 1 AndAlso pointed + firstfile <= lfiles Then
					shootfile = pointed + firstfile
					Do
						GetMouse xm, ym, wm, bm
					Loop Until bm <> 1
				End If
				If wm <> exwm Then
					firstfile += exwm - wm
					If firstfile + 28 > lfiles Then firstfile = lfiles - 28
					If firstfile < 1 Then firstfile = 1
					redrawlist = -1
				End If
			Else
				pointed = -1
				If expointed <> -1 Then
					expointed = -1
					redrawlist = -1
				End If
			End If
			
			exxm = xm : exym = ym
			exbm = bm : exwm = wm
		End If
		
		Select Case MaintainButtons
			Case 1 : akey = Chr(12)
			Case 2 : akey = Chr(19)
			Case 3
				Do : GetMouse xm, ym, wm, bm : Loop Until bm = 0
				akey = Chr(3)
		End Select
		
		Select Case akey
			Case Chr(255, 75)
				If editing Then
					If editpos > 0 Then
						editpos -= 1
						redrawedits = -1
					End If
				End If
			Case Chr(255, 77)
				If editing Then
					If editpos < Len(edline(editing)) Then
						editpos += 1
						redrawedits = -1
					End If
				End If
			Case Chr(8)
				If editing Then
					If editpos > 0 Then
						edline(editing) = Left(edline(editing), editpos - 1) + Mid(edline(editing), editpos + 1)
						editpos -= 1
						redrawedits = -1
					End If
				End If
			Case Chr(9), Chr(255, 72), Chr(255, 80)
				If editing Then
					editing = 3 - editing
					editpos = Len(edline(editing))
					redrawedits = -1
				End If
			Case " " To Chr(126)
				If editing <> 0 And Len(edline(editing)) < 62 Then
					edline(editing) = Left(edline(editing), editpos) + akey + Mid(edline(editing), editpos + 1)
					editpos += 1
					redrawedits = -1
				End If
			Case Chr(255, 83)
				If editing <> 0 AndAlso editpos < Len(edline(editing)) Then
					edline(editing) = Left(edline(editing), editpos) + Mid(edline(editing), editpos + 2)
					redrawedits = -1
				End If
			Case Chr(255, 71)
				If editing Then
					editpos = 0
					redrawedits = -1
				End If
			Case Chr(255, 79)
				If editing Then
					editpos = Len(edline(editing))
					redrawedits = -1
				End If
			Case Chr(13)
				If editing = 1 Then
					ChDir edline(1)
					edline(1) = CurDir
					editpos = Len(edline(1))
					readfiles = -1
					redrawedits = -1
					redrawlist = -1
				End If
			Case Chr(12)	'Ctrl+L - Load
				s = Config("stunpack")
				n = InStrRev(edline(2), ".")
				If n Then
					File_ErNum = -100
					Select Case LCase(Mid(edline(2), n))
						Case ".obj" : LoadOBJ edline(2)
						Case ".vsh" : LoadVSH edline(2)
						Case ".3sh" : Load3SH edline(2)
						Case ".res" : LoadRES edline(2)
						Case ".car" : LoadCar edline(2)
						Case ".p3s"
							If Len(s) Then
								Shell s & " " & edline(2) & " " & workdir & "temp.3sh"
								Load3SH workdir & "temp.3sh"
							End If
						Case ".pvs"
							If Len(s) Then
								Shell s & " " & edline(2) & " " & workdir & "temp.vsh"
								LoadVSH workdir & "temp.vsh"
							End If
						Case ".pre"
							If Len(s) Then
								Shell s & " " & edline(2) & " " & workdir & "temp.res"
								LoadRES workdir & "temp.res"
							End If
						Case Else
							If ftype = 2 Then
								outfile = "L:" & edline(2)
								Exit Do
							Else
								File_ErNum = -1
								File_Error = "Unknown format"
								File_File = ""
							End If
					End Select
					Line (650, 150)- Step (249, 15), B_COL, BF
					If File_ErNum = 0 Then
						Draw String (650, 150), "Loaded " & File_File, RGB(0, 220, 100)
					ElseIf File_ErNum <> -100 Then
						Draw String (650, 150), File_Error & ": " & File_File, RGB(220, 80, 0)
					End If
				ElseIf Len(edline(2)) = 4 Then
					File_ErNum = -100
					LoadRES "car" & LCase(edline(2)) & ".res"
					Load3SH "st" & LCase(edline(2)) & ".3sh"
					LoadVSH "stda" & LCase(edline(2)) & ".vsh"
					LoadVSH "stdb" & LCase(edline(2)) & ".vsh"
					Line (650, 150)- Step (249, 15), B_COL, BF
					If File_ErNum = 0 Then
						Draw String (650, 150), "Loaded " & edline(2), RGB(0, 220, 100)
					ElseIf File_ErNum <> -100 Then
						Draw String (650, 150), File_Error & ": " & edline(2), RGB(220, 80, 0)
					End If
				End If
			Case Chr(19)	'Ctrl+S - Save
				n = InStrRev(edline(2), ".")
				If n Then
					File_ErNum = -100
					Select Case LCase(Mid(edline(2), n))
						Case ".obj" : SaveOBJ edline(2)
						Case ".vsh" : SaveVSH edline(2)
						Case ".3sh" : Save3SH edline(2)
						Case ".res" : SaveRES edline(2)
						Case ".car" : SaveCar edline(2)
						Case Else
							If ftype = 2 Then
								outfile = "S:" & edline(2)
								Exit Do
							Else
								File_ErNum = -1
								File_Error = "Unknown format"
								File_File = ""
							End If
					End Select
					Line (650, 150)- Step (249, 15), B_COL, BF
					If File_ErNum = 0 Then
						Draw String (650, 150), "Saved " & File_File, RGB(0, 220, 100)
					ElseIf File_ErNum <> -100 Then
						Draw String (650, 150), File_Error & ": " & File_File, RGB(220, 80, 0)
					End If
				ElseIf Len(edline(2)) = 4 Then
					File_ErNum = -100
					SaveRES "car" & LCase(edline(2)) & ".res"
					Save3SH "st" & LCase(edline(2)) & ".3sh"
					SaveVSH "stda" & LCase(edline(2)) & ".vsh"
					SaveVSH "stdb" & LCase(edline(2)) & ".vsh"
					Line (650, 150)- Step (249, 15), B_COL, BF
					If File_ErNum = 0 Then
						Draw String (650, 150), "Saved " & edline(2), RGB(0, 220, 100)
					ElseIf File_ErNum <> -100 Then
						Draw String (650, 150), File_Error & ": " & File_File, RGB(220, 80, 0)
					End If
				End If
			Case Chr(3)
				If ftype = 1 Then
					If exts = "cars" Then
						exts = barecarexts
					Else
						exts = "cars"
					End If
					readfiles = -1
				End If
			Case Chr(27), Chr(255) & "k"
				curmenu = -1
				Exit Do
		End Select
	
		If MaintainMenu < 0 Then Exit Do
				
		'Open files and change directory
		If shootfile Then
			Select Case lfile(shootfile).type
				Case 0
					edline(2) = lfile(shootfile).name
					If editing = 2 Then editpos = Len(edline(2))
					redrawlist = -1
					redrawedits = -1
				Case 1, 3
					ChDir lfile(shootfile).name
					edline(1) = CurDir
					If editing = 1 Then editpos = Len(edline(1))
					readfiles = -1
					redrawlist = -1
					redrawedits = -1
				Case 2	'Drives
					ChDir lfile(shootfile).name & "."
					edline(1) = CurDir
					If editing = 1 Then editpos = Len(edline(1))
					readfiles = -1
					redrawlist = -1
					redrawedits = -1
			End Select
			shootfile = 0
		End If
	Loop
	
	buttons = 0
	Line (0, 0)-(959, 599), B_COL, BF
	
	Return outfile
End Function


ScreenRes 960, 648, 32
Width 960 \ 8, 648 \ 16
WindowTitle PROGRAM_TITLE
Line (0, 0)-(959, 647), B_COL, BF

InitParamTargets
Init3DWindows
InitDashItems
LoadConfig
LoadMaterials
LoadPalette
LoadVSH progdir & "default.vsh"

Randomize Timer

menuimg = TargaLoad(progdir & "images.tga")
MaintainMenu 1
Do
    Sleep 10
	Select Case curmenu
		Case -1 : Exit Do
		Case 0 : FileHandling 'MainPage
		Case 1 : Edit3D
		Case 2 : DashManager
		Case 3 : EditParams
		Case 4 : ViewManual
		Case Else :	MaintainMenu
	End Select
Loop

ImageDestroy menuimg
For i As Byte = 1 To bitmaps
	Deallocate bitmap(i).i
	ImageDestroy bitmap(i).i2
	ImageDestroy bitmap(i).i3
Next i
