Firstly I apologise for indenting on the code with this post but at the moment it is very difficult to manage. I have asked Euro_Snoopy to have a look at how we may improve this for the future.
Please Note: You may download the project files from the link at the bottom of the page to save yourself the time of having to create it from scratch. It also includes a MS Word version of this lesson.
Last week we looked at the format and structure of the mission file and how easily it could be edited through Notepad. This week we will construct a simple little VB interface that will edit the same values from the same mission as last week and then we will create an executable file from it. Next week we will look at expanding the programme to read a variety of missions and edit a greater variety of values.
IMPORTANT: I strongly recommend backing up the original mission file before commencing with this lesson!
There is almost no error trapping present in the code included in this lesson and with the type of controls we are using with the interface, it is possible for the user to type inappropriate values that would cause the mission file to not operate correctly with AEP. Therefore avoid typing values into the controls and instead select the default values offered by the lists, this will ensure correct values are passed to the mission file.
Remember to hit the save button frequently when working through this lesson.
Ok, so lets start VB and create a new project. Select “Standard EXE” as shown below.
Now create a new form and name it “frmMisEditor” and set the caption property to “AEP Mission Editor”, then place 3 combo box controls accompanied by 3 label controls on to the form. Next place 2 text box controls accompanied by 2 label controls on to the form. Finally place 2 command button controls on to the form. Arrange the controls approximately as shown in the image below.
Now we will name the controls with some form of logic so that it will easier to identify them with later use. Name the first label at the top left to “lblTime” and its buddy combo box to “cboTime”. As you can see the control names are constructed with a control type identifier (“lbl” for Label) with the fisrt 3 characters and the final portion describes what the control will be used for “Time” will obviously handle mission time values. The text box control will be named with the same logic as will the command buttons. So working from left to right and from top to bottom the control names will be.
lblTime
lblPlaneQty
cboTime
cboPlaneQty
lblPlane
cboPlane
lblAltitude
txtAltitude
lblSpeed
txtSpeed
btnApply
btnExit
Then place a list box control to the right of the existing controls and name it “lstMisFile”. Next resize the form and set the caption properties of the controls to closely match what is shown in the screen shot below.
Ok now is a good time to save the project. Set up a folder specifically to hold the project and all of its future components called “AEP Mission Editor Project” and save the form file into that project folder as shown below.
If asked “Add this project to SourceSafe?” click no.
Now it’s time to start the coding.
Firstly lets set up a method for exiting the programme. Double click on the “Exit” button and place code shown in blue as shown below.
Private Sub btnExit_Click()
- Unload Me
“Unload Me” is the method by which we close the application and release all associated objects from memory.
Next lets build the procedures that will handle populating the combo box controls with default values.
Create a new procedure named “PopTime” and place within it the code shown below in blue.
Public Sub PopTime()
- cboTime.AddItem "1.0"
cboTime.AddItem "2.0"
cboTime.AddItem "3.0"
cboTime.AddItem "4.0"
cboTime.AddItem "5.0"
cboTime.AddItem "6.0"
cboTime.AddItem "7.0"
cboTime.AddItem "8.0"
cboTime.AddItem "9.0"
cboTime.AddItem "10.0"
cboTime.AddItem "11.0"
cboTime.AddItem "12.0"
cboTime.AddItem "13.0"
cboTime.AddItem "14.0"
cboTime.AddItem "15.0"
cboTime.AddItem "16.0"
cboTime.AddItem "17.0"
cboTime.AddItem "18.0"
cboTime.AddItem "19.0"
cboTime.AddItem "20.0"
cboTime.AddItem "21.0"
cboTime.AddItem "22.0"
cboTime.AddItem "23.0"
cboTime.AddItem "24.0"
cboTime.ListIndex = 11
Then a new procedure named “PopPlaneQty” and place within it the code shown below in blue.
Public Sub PopPlaneQty()
- cboPlaneQty.AddItem "1"
cboPlaneQty.AddItem "2"
cboPlaneQty.AddItem "3"
cboPlaneQty.AddItem "4"
cboPlaneQty.ListIndex = 3
Then a new procedure named “PopPlaneType” and place within it the code shown below in blue.
Public Sub PopPlaneType()
- cboPlane.AddItem "BI_1"
cboPlane.AddItem "GLADIATOR1J8A"
cboPlane.AddItem "HurricaneMkIa"
cboPlane.AddItem "HurricaneMkIIb"
cboPlane.AddItem "HurricaneMkIIc"
cboPlane.AddItem "HurricaneMkIIbMod"
cboPlane.AddItem "I_153_M62"
cboPlane.AddItem "I_153P"
cboPlane.AddItem "I_16TYPE18"
cboPlane.AddItem "I_16TYPE24"
cboPlane.AddItem "I_185M71"
cboPlane.AddItem "I_185M82A"
cboPlane.AddItem "IL_2_1940Early"
cboPlane.AddItem "IL_2_1940Late"
cboPlane.AddItem "IL_2_1941Early"
cboPlane.AddItem "IL_2_1941Late"
cboPlane.AddItem "IL_2I"
cboPlane.AddItem "IL_2MEarly"
cboPlane.AddItem "IL_2MLate"
cboPlane.AddItem "IL_2T"
cboPlane.AddItem "IL_2Type3"
cboPlane.AddItem "IL_2Type3M"
cboPlane.AddItem "LAGG_3SERIES4"
cboPlane.AddItem "LAGG_3SERIES29"
cboPlane.AddItem "LAGG_3SERIES35"
cboPlane.AddItem "LAGG_3SERIES66"
cboPlane.AddItem "LAGG_3IT"
cboPlane.AddItem "LA_5"
cboPlane.AddItem "LA_5F"
cboPlane.AddItem "LA_5FN"
cboPlane.AddItem "LA_7"
cboPlane.AddItem "LA_7B20"
cboPlane.AddItem "MIG_3EARLY"
cboPlane.AddItem "MIG_3UD"
cboPlane.AddItem "MIG_3UB"
cboPlane.AddItem "MIG_3SHVAK"
cboPlane.AddItem "MIG_3AM38"
cboPlane.AddItem "MIG_3U"
cboPlane.AddItem "P_11C"
cboPlane.AddItem "P_38J"
cboPlane.AddItem "P_38L"
cboPlane.AddItem "P_39N"
cboPlane.AddItem "P_39Q1"
cboPlane.AddItem "P_39Q10"
cboPlane.AddItem "P_40E"
cboPlane.AddItem "P_40EM105"
cboPlane.AddItem "P_40M"
cboPlane.AddItem "P_47D10"
cboPlane.AddItem "P_47D22"
cboPlane.AddItem "P_47D27"
cboPlane.AddItem "P_51B"
cboPlane.AddItem "P_51C"
cboPlane.AddItem "P_51D20NA"
cboPlane.AddItem "P_51D5NT"
cboPlane.AddItem "P_63C"
cboPlane.AddItem "P_80A"
cboPlane.AddItem "SPITFIRE5B"
cboPlane.AddItem "SPITFIRE5BCLP"
cboPlane.AddItem "SPITFIRE5BLF"
cboPlane.AddItem "SPITFIRE5BLFCLP"
cboPlane.AddItem "SPITFIRE9C"
cboPlane.AddItem "SPITFIRE9CCLP"
cboPlane.AddItem "SPITFIRE9E"
cboPlane.AddItem "SPITFIRE9ECLP"
cboPlane.AddItem "SPITFIRE9EHF"
cboPlane.AddItem "TB_3_4M_17"
cboPlane.AddItem "TB_3_4M_34R"
cboPlane.AddItem "YAK_1"
cboPlane.AddItem "YAK_1B"
cboPlane.AddItem "YAK_3"
cboPlane.AddItem "YAK_3P"
cboPlane.AddItem "YAK_7A"
cboPlane.AddItem "YAK_7B"
cboPlane.AddItem "YAK_9"
cboPlane.AddItem "YAK_9B"
cboPlane.AddItem "YAK_9D"
cboPlane.AddItem "YAK_9K"
cboPlane.AddItem "YAK_9M"
cboPlane.AddItem "YAK_9T"
cboPlane.AddItem "YAK_9U"
cboPlane.AddItem "YAK_9UT"
cboPlane.AddItem "A6M2"
cboPlane.AddItem "A6M5A"
cboPlane.AddItem "CR_42"
cboPlane.AddItem "BF_109E4"
cboPlane.AddItem "BF_109E4B"
cboPlane.AddItem "BF_109E7"
cboPlane.AddItem "BF_109E7NZ"
cboPlane.AddItem "BF_109F2"
cboPlane.AddItem "BF_109F4"
cboPlane.AddItem "BF_109G2"
cboPlane.AddItem "BF_109G6"
cboPlane.AddItem "BF_109G6Late"
cboPlane.AddItem "BF_109G6AS"
cboPlane.AddItem "BF_109G10"
cboPlane.AddItem "BF_109G14"
cboPlane.AddItem "BF_109K4"
cboPlane.AddItem "BF_109Z"
cboPlane.AddItem "BF_110G2"
cboPlane.AddItem "F2A_B239"
cboPlane.AddItem "FW_190A4"
cboPlane.AddItem "FW_190A5"
cboPlane.AddItem "FW_190A6"
cboPlane.AddItem "FW_190A8"
cboPlane.AddItem "FW_190A9"
cboPlane.AddItem "FW_190D9"
cboPlane.AddItem "FW_190D9LATE"
cboPlane.AddItem "FW_190F8"
cboPlane.AddItem "G50"
cboPlane.AddItem "GO_229A1"
cboPlane.AddItem "HE_111H2"
cboPlane.AddItem "HE_111H6"
cboPlane.AddItem "HE_162A2"
cboPlane.AddItem "IAR_80"
cboPlane.AddItem "IAR_81A"
cboPlane.AddItem "IAR_81C"
cboPlane.AddItem "JU_87B2"
cboPlane.AddItem "JU_87D3"
cboPlane.AddItem "JU_87D5"
cboPlane.AddItem "JU_87G1"
cboPlane.AddItem "KI_84_IA"
cboPlane.AddItem "KI_84_IB"
cboPlane.AddItem "KI_84_IC"
cboPlane.AddItem "ME_163B1A"
cboPlane.AddItem "ME_262A1A"
cboPlane.AddItem "ME_262A1AU4"
cboPlane.AddItem "ME_262A2A"
cboPlane.AddItem "TA_152H1"
cboPlane.ListIndex = 0
The procedures above simply add one item at a time to the control, all of the choices that the user will be able to choose from when using the editor. The ListIndex property sets the default selection.
Now under the Form Initialize event place the following calls to the procedures you have just created (shown in blue).
Private Sub Form_Initialize()
- Call PopPlaneType
Call PopTime
Call PopPlaneQty
Run the programme by pressing “F5”. You will see that the time has a default value of “12.0”, the plane quantity has a default value of “4” and the plane type has a default value of “BI_1”. Press the exit button to exit the programme.
From this we can now see how the combo boxes are filled with all the available choices and how the default values are applied with the controls “ListIndex” property. Next we need to get a bit more tricky and read the mission file and populate the controls from the values that are stored in the that mission file itself.
Create a procedure named “OpenMisFile” and place within it the code shown in blue below.
Public Sub OpenMisFile()
- Dim FileData As String, MisTime As String, PlaneQty As String, PlaneName As String
Dim Alt As String, Spd As String
Dim LoopCount As Integer, i As Integer
Dim NormFlyFound As Boolean
NormFlyFound = False
LoopCount = 1
Open "C:\Program Files\Ubi Soft\IL-2 Sturmovik Forgotten Battles\Missions\Single\GB\P-51D\P-51D_3.mis" For Input As #1
Do
Line Input #1, FileData
If LoopCount = 3 Then
MisTime = Trim(FileData)
MisTime = Right(MisTime, Len(MisTime) - InStr(MisTime, " "))
cboTime.Text = MisTime
End If
If UCase(Trim(FileData)) Like "PLANES*" Then
PlaneQty = Trim(FileData)
PlaneQty = Right(PlaneQty, Len(PlaneQty) - InStr(PlaneQty, " "))
cboPlaneQty.Text = PlaneQty
End If
If UCase(Trim(FileData)) Like "CLASS AIR*" Then
PlaneName = Right(FileData, Len(FileData) - InStr(FileData, "."))
cboPlane.Text = PlaneName
End If
If UCase(Trim(FileData)) Like "NORMFLY*" And Not NormFlyFound Then
Alt = Trim(FileData)
For i = 1 To 3
Alt = Right(Alt, Len(Alt) - InStr(Alt, " "))
Next i
txtAltitude.Text = Left(Alt, InStr(Alt, " ") - 1)
Spd = Right(Alt, Len(Alt) - InStr(Alt, " "))
If InStr(Spd, " ") Then
txtSpeed.Text = Left(Spd, InStr(Right(Spd, Len(Spd) - InStr(Spd, " ")), " ") - 1)
Else
txtSpeed.Text = Spd
End If
NormFlyFound = True
End If
lstMisFile.AddItem FileData
LoopCount = LoopCount + 1
Loop Until EOF(1)
Close #1
Now lets examine specific portions of the code as it moves down the procedure.
The Open statement allows you open a specified file to work with, in this case we wish to work with the "C:\Program Files\Ubi Soft\IL-2 Sturmovik Forgotten Battles\Missions\Single\GB\P-51D\P-51D_3.mis" file.
NOTE: The “C:\Program Files\Ubi Soft portion of the file path may be different on your machine, here I have used the default installation location.
The For Input As #1 part means that we are opening the file to input data into our programme. Each file opened needs to be identified by a tag so that we have a means of identifying it when communicating with it and when closing it. In this case we use a file tag of “#1”
Next we start a “Do Loop Until EOF” loop. EOF is a built in VB function designed to let us know when we have reached the end of the file and there is no more data to read. So in plain English this is saying “continue reading each line of data in the file until there is no more data to read.”
Next we have Line Input #1, FileData .This means that the current line of data being read from the mission file will be stored in the variable named “FileData”.
Note that the Line Input refers to #1, this is the file tag to identify the file we are reading as mentioned earlier.
So for example if the current line being read from the mission file contained the data “ Class air.P_40M” then it would be true to say that
FileData = “ Class air.P_40M”.
Next line: If LoopCount = 3 Then .What this is saying is, if we are currently reading the third line of data from the mission file then execute what ever code resides within the “If" to "End If” statements. In this case we will be dealing with the data that relates to time because the time value is always stored on the third line of the mission file.
Next Line: MisTime = Trim(FileData) .Here we trim off any unwanted space characters from the line of data stored within the FileData variable and store the result in the” MisTime” variable. In this case the variable FileData will contain data something like ” TIME 10.0”. So the result would be of this be
The variable MisTime would hold the data “TIME 10.0” because the 2 space characters have been removed from the left hand side.
Next Line: MisTime = Right(MisTime, Len(MisTime) - InStr(MisTime, " ")) Ok, this is a little more difficult to read but means that we are removing the “10.0” portion of the “TIME 10.0” that is stored in the variable “MisTime” and then passing that value back to the source variable again so that “MisTime” will then hold the data “10.0”. Lets look more closely at this particular line of code.
The Right function is a VB built in function that returns the right most portion of a string with a specified number of characters. Example: Right(“TIME 10.0”, 4) would return “10.0” because “10.0” is the last 4 characters in the source string “TIME 10.0”.
The Len function is a VB built in function that returns a number that indicates how many characters are in a string.
Example: Len(“TIME 10.0”) would return 9.
The InStr function is a VB built in function that returns a number that represents the position of one string within another.
Example: Instr(“TIME 10.0, “ ”) would return 5 because the space
character “ “ is the fifth character in the source string “TIME 10.0”.
Now to explain more clearly lets stack the lines of code to show equivalents:
MisTime = Right (MisTime, Len(MisTime) - InStr(MisTime, " "))
MisTime = Right (“TIME 10.0”, 9 - 5 )
So… 9 – 5 = 4 and the 4 characters on the right hand side of the string “TIME 10.0” are “10.0”. This means that the variable “MisTime” will hold the value “10.0”
These principles are use frequently in this lesson so it is vital that you obtain a good understanding of these before you continue.
Next Line: cboTime.Text = MisTime. Knowing now that the variable “MisTime” will hold a value similar to “10.0” we can pass that value the cboTime control which will then display a value that will make some sense to the user.
IMPORTANT: As we progress through the lesson I will only cover lines of code that have content that has not had the working principles explained previously.
Line: If UCase(Trim(FileData)) Like "PLANES*" Then. The Like operator is a VB built in operator performs a comparison between two strings and supports the use of wildcard characters. The Ucase function converts all characters to uppercase.
So if the variable contained data equal to ” Planes 4” the expression
Ucase(Trim(FileData)) would return “PLANES 4”. UCase is very good to use for string comparisons when you are unsure of the character casing. Now we use the Like operator to carry out the comparison. So if Ucase(Trim(FileData)) is like “PLANES*” then the comparison is true because the wildcard character (*) is saying “I don’t give a damn what comes after PLANES just so long as the bit before the wildcard character (*) equals PLANES” and this case it does.
Line: For i = 1 To 3. This sets up a loop that will persist for 3 cycles 1,2 then 3. the variable “i” is a user defined variable to hold integer values. So what this line is saying in plain English is “loop 3 times starting with 1 and ending on the completion of the third cycle.
Line: Alt = Right(Alt, Len(Alt) - InStr(Alt, " ")). Even though I have already explained the principle of this line of code previously, I will explain it again because this time it is used within a loop which may be confusing.
The variable “Alt” will hold data similar to:
“NORMFLY 17375.10 26628.11 3000.00 400.00”. Because the line of code above is designed to return all the characters to the right of the first space character (“ “) found, if this is repeated 3 times the return result would be “3000.00 400.00”.
So to do it step by step:
First time through the loop returns “17375.10 26628.11 3000.00 400.00”
Second time through the loop returns “26628.11 3000.00 400.00”
Third time through the loop returns “3000.00 400.00”
Line: NormFlyFound = True. This sets the Boolean variable “NormFlyFound” to equal true. Because Not NormFlyFound is a condition to break down the altitude and speed data, this ensures the programme will only process the first instance of altitude and speed data found in the mission file, which is what we want for this lesson.
Line: Loop Until EOF(1). This stops the process of reading data from the mission file because there is no more data to read. Note the file tag “1”.
Line: Close #1. This closes the file and frees it from memory.
Again note the file tag “#1”.
Now Create a procedure named “UpDateList” and place within it the code shown in blue below.
Public Sub UpDateList()
- Dim i As Integer, j As Integer
Dim NormFlyLine As String, ModLine As String
Dim NormFlyFound As Boolean
NormFlyFound = False
For i = 0 To lstMisFile.ListCount - 1
If i = 2 Then
lstMisFile.RemoveItem i
lstMisFile.AddItem " TIME " + Trim(cboTime.Text), i
End If
If UCase(Trim(lstMisFile.List(i))) Like "PLANES*" Then
lstMisFile.RemoveItem i
lstMisFile.AddItem " Planes " + Trim(cboPlaneQty.Text), i
End If
If UCase(Trim(lstMisFile.List(i))) Like "CLASS AIR*" Then
lstMisFile.RemoveItem i
lstMisFile.AddItem " Class air." + Trim(cboPlane.Text), i
End If
If UCase(Trim(lstMisFile.List(i))) Like "NORMFLY*" And Not NormFlyFound Then
NormFlyLine = Trim(UCase(Trim(lstMisFile.List(i))))
For j = 1 To 3
ModLine = ModLine + Left(NormFlyLine, InStr(NormFlyLine, " "))
NormFlyLine = Right(NormFlyLine, Len(NormFlyLine) - InStr(NormFlyLine, " "))
Next j
ModLine = " " + ModLine + Trim(txtAltitude.Text) + " " + Trim(txtSpeed.Text)
lstMisFile.RemoveItem i
lstMisFile.AddItem ModLine, i
NormFlyFound = True
End If
Next i
Now lets examine lines of the code in this procedure that have not yet been covered.
Line: For i = 0 To lstMisFile.ListCount - 1. This procdues a loop similar to that covered earlier except that this time the end of the loop is determined by the quantity of entries in the “lstMisFile” list box control. Because the “ListCount” property counts from 1 upward and the “ListIndex” property starts from 0 the last item in the list will have a ListIndex value that is always equal to ListCount – 1. So in plain English the line of code above is saying “loop from the first entry in the list box control through to the last entry in the list box control”.
Line: lstMisFile.RemoveItem i. This removes the item in the list box control that is at the current list index which is determined by the user defined variable “i”. So if “i” = 2 the third item in the list will be removed.
Line: lstMisFile.AddItem " TIME " + Trim(cboTime.Text), i. This adds a line of data to the list box control that is at the current list index which is determined by the user defined variable “i”. So if “i” = 2 the new line of data will be added to the third position in the list. So in summary the previous line of data is removed and replaced with the new user defined data from the cboTime combo box control.
Example: If the “cboTime” combo box control had a value of “12.0” in it then
“ TIME “ + “12.0” would produce a string value of “ TIME 12.0”. That data would then be placed into the third position in the “lstMisFile” list box control.
Now Create a procedure named “UpDateList” and place within it the code shown in blue below.
Public Sub UpDateMisFile()
- Dim i As Integer, j As Integer
Dim NormFlyLine As String, ModLine As String
Dim NormFlyFound As Boolean
NormFlyFound = False
Open "C:\Program Files\Ubi Soft\IL-2 Sturmovik Forgotten Battles\Missions\Single\GB\P-51D\P-51D_3.mis" For Output As #1
For i = 0 To lstMisFile.ListCount - 1
If i = 2 Then
Print #1, " TIME " + Trim(cboTime.Text)
ElseIf UCase(Trim(lstMisFile.List(i))) Like "PLANES*" Then
Print #1, " Planes " + Trim(cboPlaneQty.Text)
ElseIf UCase(Trim(lstMisFile.List(i))) Like "CLASS AIR*" Then
Print #1, " Class air." + Trim(cboPlane.Text)
ElseIf UCase(Trim(lstMisFile.List(i))) Like "NORMFLY*" And Not NormFlyFound Then
NormFlyLine = UCase(Trim(lstMisFile.List(i)))
For j = 1 To 3
ModLine = ModLine + Left(NormFlyLine, InStr(NormFlyLine, " "))
NormFlyLine = Right(NormFlyLine, Len(NormFlyLine) - InStr(NormFlyLine, " "))
Next j
ModLine = " " + ModLine + Trim(txtAltitude.Text) + " " + Trim(txtSpeed.Text)
Print #1, ModLine
NormFlyFound = True
Else
Print #1, lstMisFile.List(i)
End If
Next i
Close #1
At this stage I feel that the principles of the above procedure have already been covered so it’s probably just a case of reading and examining the code to see that this is essentially just the reverse of reading the file to write out the updated data back to disk.
Right then, lets get this programme working. Double click on the “btnApply” command button control on the form and call the 2 procedures as shown below in blue.
Private Sub btnApply_Click()
- Call UpDateList
Call UpDateMisFile
End Sub
Ok, that’s it for this week now lets compile and make the executable. Goto the file menu and select “Make …. Exe…” Save the executable into the project folder as shown below and give it a test run.
Next week we will expand the editor to open a variety of mission files and expand the scope of data it can edit. Until then happy coding.
The link to the project files.
http://airwarfare.com/AWX/Files/cs/VB%20Lesson%202.zip
Cheers, CrazySchmidt.