Results 1 to 6 of 6

Thread: RailDriver New Light Switch Code v0.2b

  1. #1
    Join Date
    Mar 2011
    Location
    Florida, USA
    Posts
    11

    Default RailDriver New Light Switch Code v0.2b

    Ok, I've got some new code here. Please read everything below. This should be a significant improvement over the previous code I put together.

    George’s RailDriver Light Switch Replacement Code v0.2b
    This document was last updated on: October 12, 2011

    Changes from v0.1b (original release)
    - Complete code re-write to better determine switch position. The script now does a much better job of detecting the current switch position.

    - Added LED Support. The LED now displays the position of the switch, as determined by the script. The purpose is two-fold.
    1) You can easily see which code block the script actually ran, and 2) it acts as an indicator so you know that the script has completed running. The LED output should be self-explanatory, but I’ll break it down for you here.
    L-0 (Switch is in the ‘Off’ position)
    L-1 (Switch is in the ‘Dim’ position)
    L-2 (Switch is in the ‘Full’ position)
    You will want to wait until the indicator changes back to displaying the current speed, before changing the switch to a different position.




    Known Limitations
    - There is no support for ‘position hopping’. In other words, the script expects you to stop at each position before moving to the next one. Jumping from the ‘Off’ position to the ‘Full’ position (or vice-versa) will not work properly and can cause odd behavior from the script. If you are switching from ‘Off’ to ‘Full’, you MUST stop at ‘Dim’ (position 1) and let that segment of script run, before moving to the ‘Full’ position. You will know the script for the current position has completed when the switch position indicator on the LED has gone back to displaying speed.


    - You MUST let the currently executing script finish what it is doing, before changing to a new position. ALWAYS wait for the Light Switch Position indicator to appear on the LED, then wait for it to go back to displaying the current speed, before switching positions.


    - Turning the knob too slowly can cause the script to not execute. Just turn the knob normally from one position to the next, and there should be no problem. If the switch doesn’t respond, turn it back to the ‘Off’ position, wait a couple of seconds, then turn it again. It should start responding again. This can easily be avoided by changing the default thresholds from 4 to 0 as outlined in the ‘Recommendations’ section of this document.


    - When turning on the front headlights from the ‘Off’ position, there is a delay before the lights actually turn on. This is because of the way I had to work around the raildriver.dll options, by sending simulated keystrokes to the sim, instead of being able to directly send a value through the dll file. Obviously, if there were a way to properly set the variable, instead of it just being a toggle, there would have been no need for me to try and work around this limitation by modifying the script. In any case, the delay is there to try to prevent ‘position drift’ (where a switch position is acting like a different position), by cycling the headlights off before being turned on for the front (or ‘Dim’) position. From the ‘Off’ position, this gives the appearance of a delay, but in actuality, the code is just making sure the lights actually are off, before turning them on for the front position.


    - When turning on the front headlights from the ‘Full’ position, the headlights flash off before coming back on. This is normal, expected behavior. See the paragraph above this one, as it explains that when the switch changes to the front (or ‘Dim’) position, the lights are cycled off before being set to the front position.


    - Occasionally, when the speed hasn’t changed (like when the train isn’t moving,) the L code may get ‘stuck’ on the LED. You can correct this by either switching positions and going back again, or just wait until the train starts to move, and it will start displaying the speed again.






    Recommendations
    - For best results, it is highly recommended that you change the default thresholds to 0 as the script creates and uses its own thresholds. The section that should be changed is as follows:


    Find this section (about 80% of the way down the script)
    Code:
    lever(6).position(0).min = minvals(19) 'rotary 2 position 1 (off)
            lever(6).position(0).max = maxvals(19) 'rotary 2 position 1 (off)
        lever(6).position(0).upperthreshold=4 'must not be 0 w/o changing the logic below
        lever(6).position(0).lowerthreshold=4 'must not be 0 w/o changing the logic below
        lever(6).position(1).min = minvals(20) 'rotary 2 position 2 (slow)
            lever(6).position(1).max = maxvals(20) 'rotary 2 position 2 (slow)
        lever(6).position(1).upperthreshold=4 'must not be 0 w/o changing the logic below
        lever(6).position(1).lowerthreshold=4 'must not be 0 w/o changing the logic below
        lever(6).position(2).min = minvals(21) 'rotary 2 position 1 (full)
            lever(6).position(2).max = maxvals(21) 'rotary 2 position 1 (full)
        lever(6).position(2).upperthreshold=4 'must not be 0 w/o changing the logic below
        lever(6).position(2).lowerthreshold=4  'must not be 0 w/o changing the logic below





    Then change the thresholds from 4 to 0, so that it reads as follows:
    Code:
    lever(6).position(0).min = minvals(19) 'rotary 2 position 1 (off)
            lever(6).position(0).max = maxvals(19) 'rotary 2 position 1 (off)
        lever(6).position(0).upperthreshold=0 'Default value=4, must not be 0 w/o changing the logic below
        lever(6).position(0).lowerthreshold=0 'Default value=4, must not be 0 w/o changing the logic below
        lever(6).position(1).min = minvals(20) 'rotary 2 position 2 (slow)
            lever(6).position(1).max = maxvals(20) 'rotary 2 position 2 (slow)
        lever(6).position(1).upperthreshold=0 'Default value=4, must not be 0 w/o changing the logic below
        lever(6).position(1).lowerthreshold=0 'Default value=4, must not be 0 w/o changing the logic below
        lever(6).position(2).min = minvals(21) 'rotary 2 position 1 (full)
            lever(6).position(2).max = maxvals(21) 'rotary 2 position 1 (full)
        lever(6).position(2).upperthreshold=0 'Default value=4, must not be 0 w/o changing the logic below
        lever(6).position(2).lowerthreshold=0 'Default value=4, must not be 0 w/o changing the logic below





    Things I’ve Learned


    I can’t say if this is a MacroWorks 3 issue, or a Train Simulator 2012 (RailWorks 3) issue, however during my debugging, I noticed that for no reason that I can pin down, the RailDriver re-reads the configuration of the current train while a scenario is already in progress. This, in and of itself, would not be an issue, but there are times when the information it gets back is wrong, thereby affecting the operation of the unit. For example, I’ve seen the code receive data showing that there should be a dynamic brake when a dynamic brake doesn’t exist. I don’t know if this is something in the MacroWorks 3 software that is causing it to request the data again, or if Train Simulator 2012 itself is sending a ‘train changed’ event or something similar.


    In any case, this could easily be worked around IF the Train Simulator 2012 developers were to update the Raildriver.dll file so that it would include some kind of an identifier for each locomotive, and IF the script for the RailDriver itself in MacroWorks 3 were to be updated to identify lever configurations based on the individual locomotive.


    How this affects me is that, just like the default RailDriver script, I have to read variables to determine what configuration of throttle and brake types are present, in order to make timing exceptions for certain locomotives, to have as much compatibility as possible.


    When the information that comes back is incorrect, then the timings used will change and cause the lights to behave erratically. The throttle, brakes and/or reverser will also usually be affected at this time as well. Hitting the ESC key to bring up the menu, then resuming play from there usually straightens things out, as that apparently causes the RailDriver script to re-read the values (and hopefully get all the correct values back.)

  2. #2
    Join Date
    Mar 2011
    Location
    Florida, USA
    Posts
    11

    Default RailDriver New Light Switch Code v0.2b - Code Installation

    I had intended to add this as a reply to my previous post (so mods, if you could merge the two, it would be appreciated), but the first post has to wait for moderator approval (as I'm sure this one will too), so I wanted to get them both up together.

    The Code - Installation


    Below is the replacement light code. You should have already performed the changes that were recommended previously, in the Recommendations section. If you do not perform those steps, the script will still work, but the responsiveness can decrease
    and “position drift” (where one switch position is acting as another) is more likely to happen.


    Make sure you have backed up your default script before making any changes. I am not responsible if you don’t have a backup if things go wrong. In MacroWorks 3, choose “File -> Save As” and save a copy of the script someplace you’ll easily remember, (like
    your ‘Documents’ folder, and give it a name that you can easily identify later.


    Once you have backed up the default script, in the MacroWorks interface, click on “View -> Advanced View”.


    Scroll down to the section that reads “Case 2007”, and highlight from there down to the line that reads “End Select 'Analog Check End [/MWTAG13]”. Then replace that with the code in the next post I make.


    (Please go to the next post on this for the actual code.)



    The following locomotives have been tested and work on both my laptop and my main pc. The specs are quite far apart, so they are a good representation of opposing ends of the spectrum of pc performance.


    Invincible
    7F 2-8-0
    BR101
    BR143
    BR151
    BR294
    BR52 2-10-0
    Big Boy (Properly activates on HUD, but lights are fixed)
    Black 5 4-6-0
    Class 166
    Class 37
    Class 43 (Just Trains)
    Class 450
    Class 47
    Class 483
    Class 55
    EMD F7
    EMD GP7
    EMD SD40-2
    GE ES44AC
    GP40
    HST BR Blue
    HST FGW Blue
    Permaculp Carrier
    Super Express Concept


    The following locomotives do not work correctly:
    B&M E7a - Lights will frequently show pos 1 is active on the HUD, but the lights only flicker on then go off.


    BRV200 - This could be fixed if I could make an exception that applied to this loco only, as it is a timing issue. But fixing the timing for this one breaks it for four other locos, so I felt it was better to leave this one not working, and four others working.


    The following locomotives are not tested because I have no scenarios to test them on:
    Class 43 (RSC)
    Consolidation 2-8-0 (Scenario I have fails to load.)
    DB BR 151 002-3


    Those represent all the locomotives I currently have. So no other locomotives have been tested. If it's not listed here, I don't have it and can't test it.


    Hope this is helpful to my fellow RailDriver users.


    -George

  3. #3
    Join Date
    Mar 2011
    Location
    Florida, USA
    Posts
    11

    Default

    The Code - Installation


    Below is the replacement light code. You should have already performed the changes
    that were recommended previously, in the Recommendations section. If you do not
    perform those steps, the script will still work, but the responsiveness can decrease
    and “position drift” (where one switch position is acting as another) is more likely to
    happen.


    Make sure you have backed up your default script before making any changes. I am
    not responsible if you don’t have a backup if things go wrong. In MacroWorks 3, choose
    “File -> Save As” and save a copy of the script someplace you’ll easily remember, (like
    your ‘Documents’ folder, and give it a name that you can easily identify later.
    Once you have backed up the default script, in the MacroWorks interface, click on
    “View -> Advanced View”.


    Scroll down to the section that reads “Case 2007”, and highlight from there down to
    the line that reads “End Select 'Analog Check End [/MWTAG13]”. Then replace that
    with the following:
    Code (First part since it would not all fit into one reply)
    Code:
    		Case 2007 '3 Position Rotary Switch 2
    			Dim LightSWPosSensitivity as Integer 'Used to create a buffer around the Off and Full positions
    			Dim LightSWDimSensitivity as Integer 'Used to create a buffer around the Dim position
    			Dim HoldKeyTime as Integer 'Used to determine how long a key press is held down
    			Dim HoldKeyDoubleTime as Integer 'Used for double-length keypresses
    			Dim CurLightSWPos as Integer 'Values are 0=Off 1=Dim 2=Full 3=Not In Range
    			Dim LightSwitchPosChanged as Boolean 'Used to verify switch is in a different position
    			Dim LtSwLastAnalog as Integer 'Used to check if switch is still moving
    			Dim LtSwLEDVal as Integer=0
    			Dim LtOffLo as Integer
    			Dim LtOffHi as Integer
    			Dim LtDimLo as Integer
    			Dim LtDimHi as Integer
    			Dim LtFullLo as Integer
    			Dim LtFullHi as Integer
    			Dim LtSwFirstRun as Boolean
    			Dim LtSwWriteToLED as Boolean
    			Dim LtSwSkipThrottleStyleCheck as Boolean = False
    
    
    			'Locomotive features variables used for creating exceptions to timing rules
    			Dim LtSwCombinedThrottle as Boolean = False
    			Dim LtSwGearLever as Boolean = False
    			Dim LtSwTrainBrake as Boolean = False
    			Dim LtSwLocoBrake as Boolean = False
    			Dim LtSwDynamicBrake as Boolean = False
    
    
    			'Debugging variables
    			Dim LightSwitchDebug as Boolean	
    			Dim LightSwitchReportedValue as String		
    			Dim LtOffLoDebug as String
    			Dim LtOffHiDebug as String
    			Dim LtDimLoDebug as String
    			Dim LtDimHiDebug as String
    			Dim LtFullLoDebug as String
    			Dim LtFullHiDebug as String
    			
    			LightSwitchDebug=False 'Should be set to False or message boxes will appear every time the switch changes positions
    
    
    			'Tweakable values to modify sensitivity of each position and length of time keys are held down
    			LightSWPosSensitivity = 5 'the higher the number, the broader the acceptable range for off and full positions
    			LightSWDimSensitivity = 20 'Same as above but only for the dim position to account for drifting values coming from the center point of the switch
    			HoldKeyTime = 250 'Base value for holding keys for this amount of time in milliseconds and gets adjusted later in the script based on ThrottleStyle
    
    
    			'Do not change anything below this line as it will break the script
    			LightSwitchPosChanged = False
    			
    			'Check for switch movement			
    			If (AnalogValue(0)<>lever(6).LastValue) Then	'Switch movement detected
    
    
    				'Determine Locomotive Features
    				If (RailSimCombinedThrottleBrakeMin<>0 Or RailSimCombinedThrottleBrakeMax<>0) Then 'Combined Throttle
    					LtSwCombinedThrottle=True
    					'MsgBox("Combined Throttle Exists",1)
    				Else
    					LtSwCombinedThrottle=False
    				End If
    
    
    				If (RailSimGearLeverMin<>0 Or RailSimGearLeverMax<>0) Then 'Gear lever
    					LtSwGearLever=True
    					'MsgBox("Gear Lever Exists.",1)
    				Else
    					LtSwGearLever=False
    				End If
    
    
    				If (RailSimTrainBrakeMin<>0 Or RailSimTrainBrakeMax<>0) Then 'Train Brake
    					LtSwTrainBrake=True
    					'MsgBox ("Train Brake Exists.",1)
    				Else
    					LtSwTrainBrake=False
    				End If
    
    
    				If (RailSimLocoBrakeMin<>0 Or RailSimLocoBrakeMax<>0) Then 'Locomotive Brake
    					LtSwLocoBrake=True
    					'MsgBox ("Locomotive Brake Exists.",1)
    				Else
    					LtSwLocoBrake=False
    				End If
    
    
    				If (RailSimDynamicBrakeMin<>0 Or RailSimDynamicBrakeMax<>0) Then 'Dynamic Brake
    					LtSwDynamicBrake=True
    					'MsgBox ("Dynamic Brake Exists.",1)
    				Else
    					LtSwDynamicBrake=False
    				End If
    
    
    				LtSwLastAnalog=lever(6).LastValue
    				LightSwitchReportedValue=AnalogValue(0) 'Convert the analog value to a string for debugging message boxes
    
    
    				'Create ranges for switch positions
    				LtOffLo = (lever(6).position(0).min-lever(6).position(0).lowerthreshold - LightSWPosSensitivity)
    				LtOffHi = (lever(6).position(0).max-lever(6).position(0).upperthreshold + LightSWPosSensitivity)
    				LtDimLo = (lever(6).position(1).min-lever(6).position(1).lowerthreshold - LightSWDimSensitivity) 
    				LtDimHi = (lever(6).position(1).max-lever(6).position(1).upperthreshold + LightSWDimSensitivity) 
    				LtFullLo = (lever(6).position(2).min-lever(6).position(2).lowerthreshold - LightSWPosSensitivity)
    				LtFullHi = (lever(6).position(2).max-lever(6).position(2).upperthreshold + LightSWPosSensitivity)
    
    
    				LtOffLoDebug = LtOffLo
    				LtOffHiDebug = LtOffHi
    				LtDimLoDebug = LtDimLo
    				LtDimHiDebug = LtDimHi
    				LtFullLoDebug = LtFullLo
    				LtFullHiDebug = LtFullHi
    
    
    				'Check if this is the first movement of the switch
    				If (lastrp2=-1) Then
    					LtSwFirstRun=True 'First run
    				Else
    				End If
    
    
    				'Check current light switch position
    				If (AnalogValue(0)<LtOffHi) Then 'Switch is in the Off position
    					CurLightSWPos=0
    				Else If (AnalogValue(0)>LtDimLo) And (AnalogValue(0)<LtDimHi) Then 'Switch is in the Dim position
    					CurLightSWPos=1
    				Else If (AnalogValue(0)>LtFullLo) Then 'Switch is in the Full position
    					CurLightSWPos=2
    				Else 'Switch is in between positions
    					CurLightSWPos=3
    				End If
    
    
    				If (LtSwFirstRun=True) Then
    					lastrp2=CurLightSWPos 'Since this is the first run we need to set the position the switch is in at the start
    				Else
    				End If			
    
    
    				'Make sure current switch position is different than the last position
    				If (CurLightSWPos<lastrp2) Or (CurLightSWPos>lastrp2) Then 'The switch is in a different range so run the code
    					LightSwitchPosChanged=True 'The switch is in a valid range so continue running code
    				Else
    					LightSwitchPosChanged=False 'The switch is in the same range as the last position
    				End If	
    				
    				If (CurLightSWPos<>3) Then 'Switch is in a valid position
    					lastrp2=CurLightSWPos	
    				Else
    				End if

  4. #4
    Join Date
    Mar 2011
    Location
    Florida, USA
    Posts
    11

    Default

    Code Part 2 (paste in directly where you left off from the code above)

    Code:
    				If (LightSwitchPosChanged=True) Then 'The switch is in a different position so run the code below
    
    
    					'Create special case exceptions to the rules below based on locomotive features.
    					'Rule exception for BR294. This works because it is the only locomotive with a combined throttle with train and locomotive brakes
    					If (LtSwCombinedThrottle=True And LtSwTrainBrake=True And LtSwLocoBrake=True And LtSwDynamicBrake=False And LtSwGearLever=False) Then
    						HoldKeyTime=HoldKeyTime-100
    						LtSwSkipThrottleStyleCheck=True
    						'MsgBox("BR294 Detected.",1)
    					End If
    
    
    					If (LtSwSkipThrottleStyleCheck=False) Then 'no exceptions have been applied so use default ThrottleStyle timing configuration
    						'Check ThrottleStyle to modify hold key times
    						Select Case (ThrottleStyle)
    							Case 0 'throttle as labeled on RD (throttle on bottom half)
    								'MsgBox("Throttle as labeled on RD",1)
    								HoldKeyTime=HoldKeyTime-50
    							Case 1 'throttle/train brake
    								'MsgBox("Throttle-Train Brake",1)
    								HoldKeyTime=HoldKeyTime
    							Case 2 'throttle/dynamic brake
    								'MsgBox("Throttle-Dynamic Brake",1)
    								HoldKeyTime=HoldKeyTime-100
    							Case 3 'all throttle
    								'MsgBox("All Throttle",1)
    								HoldKeyTime=HoldKeyTime-50
    						End Select
    					End If 
    
    
    					HoldKeyDoubleTime = (HoldKeyTime + HoldKeyTime + 200) 'This is used to calculate how long to hold down the key to simulate an extended keypress
    			
    					Select Case (CurLightSWPos)
    						Case (0) 'Set lights to Off						
    							'SetRailSimValue(eRailSimControlID.HEADLIGHTS_ID, 1) 'toggle off headlights
    							'The following ScanCodes tell RD to send SHIFT+H to RW long enough to ensure lights turn off
    							MW3.sendScanCode("42:1,35:1") 'Press SHIFT (42) H (35)
    							MW3.insertDelay(HoldKeyDoubleTime) 'Uses double HoldKeyTime for extended keypress
    							MW3.sendScanCode("35:0,42:0") 'Release SHIFT (42) H (35)
    							LtSwLEDVal=63 'LED Value for 0
    							LtSwWriteToLED=True 'Finished this code block so it is ok to write to the LED
    							If (LightSwitchDebug=True) Then 'Switch debugging messages are turned on so display them
    								MsgBox("The current light switch position is: OFF. Current switch value is: " & LightSwitchReportedValue & ". Valid numbers for the range are: " & LtOffLoDebug & " - " & LtOffHiDebug & ".",1)
    							Else
    							End If
    						Case (1) 'Set lights to position 1							
    							MW3.sendScanCode("42:1,35:1") 'Press SHIFT (42) H (35) 'Hold down SHIFT+H long enough to turn off lights
    							MW3.insertDelay(HoldKeyDoubleTime ) 'HoldKeyTime is doubled for extended keypress
    							MW3.sendScanCode("35:0,42:0") 'Release SHIFT (42) H (35)
    							MW3.insertDelay(50) 'Small delay before sending new keystrokes
    							MW3.sendScanCode("35:1") 'Press lower-case h (35)
    							MW3.insertDelay(HoldKeyTime)
    							MW3.sendScanCode("35:0") 'Release lower-case h (35)
    							LtSwLEDVal=6 'LED Value for 1
    							LtSwWriteToLED=True 'Finished this code block so it is ok to write to the LED
    							If (LightSwitchDebug=True) Then 'Switch debugging messages are turned on so display them
    								MsgBox("The current light switch position is: DIM (Front). Current switch value is: " & LightSwitchReportedValue & ". Valid numbers for the range are: " & LtDimLoDebug & " - " & LtDimHiDebug & ".",1)
    							Else
    							End If
    						Case (2) 'Set lights to position 2
    							'SetRailSimValue(eRailSimControlID.HEADLIGHTS_ID, 1) 'toggle on headlights
    							MW3.sendScanCode("35:1") 'Press lower-case h (35)
    							MW3.insertDelay(HoldKeyDoubleTime) 'Double the HoldKeyTime for extended keypress
    							MW3.sendScanCode("35:0") 'Release lower-case h (35)
    							LtSwLEDVal=91 'LED Value for 2
    							LtSwWriteToLED=True 'Finished this code block so it is ok to write to the LED
    							If (LightSwitchDebug=True) Then 'Switch debugging messages are turned on so display them
    								MsgBox("The current light switch position is: FULL (Rear). Current switch value is: " & LightSwitchReportedValue & ". Valid numbers for the range are: " & LtFullLoDebug & " - " & LtFullHiDebug & ".",1)
    							Else
    							End If
    						Case (3) 'Light switch is in an area between positions so do nothing
    					End Select
    
    
    					'write switch position to LEDs
    					If (LtSwWriteToLED=True) Then 'OK to write to LEDs
    						Dim OutputReport() as Byte = {0,134,LtSwLEDVal,64,56,0,0,0,0}
    						Dim LtSwLEDDisplayCounter as Integer=0
    						Dim LtSwLEDWriteSpeedCounter as Integer=0
    
    
    						For LtSwLEDDisplayCounter=1 to 200
    							MW3.SendGenericOutput(MyDevice,OutputReport)
    						Next 
    
    
    						'Finished writing L code so get current speed to display
    						Dim speed As Single = GetRailSimValue(30, 0) 'speedometer
    						'make absolute and put in desired units
    						speed=Math.Abs(speed)
    
    
    						If (speedunit=True) Then speed=Math.Abs(speed*1.609344)			
    							Dim speeds As String=speed.ToString("000") 'assumes that the train will never be faster than 999, this rounds also
    
    
    							If (appactive=true) Then 'write current speed if RailWorks is still the active app
    								'write to LEDs
    								Dim AltOutputReport() as Byte = {0,134,ToDigits(speeds.Substring(2, 1)),ToDigits(speeds.Substring(1, 1)),ToDigits(speeds.Substring(0, 1)),0,0,0,0}
    								For LtSwLEDWriteSpeedCounter=1 to 5
    									MW3.SendGenericOutput(MyDevice,AltOutputReport)
    								Next
    								lastspeed=speeds
    							End If
    
    
    						LtSwLEDDisplayCounter=0
    						LtSwLEDWriteSpeedCounter=0
    						LtSwWriteToLED=False
    
    
    					Else 'Do not write to LEDs
    					End If
    					LtSwWriteToLED=False
    
    
    				Else 'The switch is in the same position as last time so do not run the code
    				End if
    
    
    			Else
    			End If 
    			lever(6).LastValue=AnalogValue(0)
    
    
    		End Select 'Analog Check End [/MWTAG13]

  5. #5
    Join Date
    Mar 2011
    Location
    Florida, USA
    Posts
    11

    Default

    Mods, sorry I did not intend to make 2 topics, if possible, please merge this one and the other one, so that this post appears as the top-most in the topic (so move the other thread into this one as replies if possible).

    Thank you.
    -George
    Last edited by GeorgeT93; 10-12-2011 at 03:50 PM.

  6. #6
    Join Date
    Mar 2011
    Location
    Florida, USA
    Posts
    11

    Default

    To any of the moderators,

    You can unsticky this post now, as the new version of Macroworks for TS2013 has now incorporated this code, so there's no longer a need for any users to manually put this code in.

    Thanks!

    -George

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •