FILE STRUCTURE

As soon as you want to add your own folksy phrase for a particular command, or localize the files, you'll need to understand these CSV files. Here's how they all look with some sample lines, and how they connect up. There are only four unique kinds of files, some are copied just for organizational purposes:

key_codes.csv

Key,Virtual Key Code,Scan Code
F3, 0x71, 0x3C
F4, 0X73, 0x3E

These are the raw codes for the various keys SH4 uses. This file has no knowledge of what the keys are used for - if you change a key mapping in the game and it's not defined here, you won't be able to reference it in the other files. Beyond that, there's no reason to edit this. The Key part must be unique, only appearing once in the file.

You can get the Virtual Key Codes from http://delphi.about.com/od/objectpascalide/l/blvkc.htm (just preface them with 0x of course). Don't worry about getting the scan code right for now, just put 0x00 in there as a placeholder until such time as they may be needed.

key_commands.csv, tubes.csv, and variables.csv

id,Type,Name,Data
2,Key,Navigation Map,F3
3,Key,Attack Periscope,F4

id is how the key command is mapped to the voice file. These must be unique, only appearing once in the file.
Type indicates how to handle this: Theres Key (for simple keystrokes), SetVar (to set internal variables), and Tube (for special tube handling)
Name is simply for your own reference, the software doesn't do anything with it.
Key should match a Key from key_codes.csv.

voice_commands.csv

id,Name,Voice Command
2,Navigation Map,Map
3,Attack Periscope,Attack Periscope
3,Attack Periscope,Raise Attack Periscope
18,Toggle Silent Running Mode,Silent Running
18,Toggle Silent Running Mode,Silence
18,Toggle Silent Running Mode,Quiet

id is the id of the command from key_commands.csv, and needs to match some id From that file.
Name is simply for your reference, the software doesn't do anything with it.
Voice Command is the actual spoken word or phrase that will trigger this command id. As you can see, you can repeat the id/Name with multiple voice command aliases for a given command, such as "silence" and "quiet". Add as many of these as you like for a given command!

command_bar.csv, dials.csv

id, Type, Name, Parent, x 1024x768, y 1024x768, x 1920x1080, y 1920x1080, Radius, Multiplier, Max, Rotated, Reversed
503,Button,Depth Under Keel, Command Room, 189, 739, 194, 1053
706,HeadingDial1,Rudder Dial,701,804,673,1700,984,46,4,38,180,0

For the button:
This means we have a command bar button or dial with ID #503 (which may have a matching voice_commands.csv entry. Many won't since they have better key equivalents.)
The type is a Tab or Button (handled the same way internally), or variants on "Dial" (which tracks which dials are active to be sure it uses the right one.)
This has a "parent" id which should be in this file somewhere. That must be pressed first before pressing this. One exception here is that Dial parents are only clicked if necessary.
The x/y coordinate in 1024x768 mode is 189x739, and the x/y coordinate in 1920x1080 mode is 194x1053. The mouse will move there and press it.
The Radius, Multiplier, Max and Inverted are only used for dials:
For the dials:
All the commands are the same as buttons. The x/y coordinates specify the center of the dial. Then there are some extras:
Name - Usually not used, but in this case, since it ends with a 1, it makes sure that the Parent is in the On state. HeadingDial0 would make sure the Parent is in the Off state. It keeps track of these so it doesn't keep clicking the dial mode flip buttons.
Radius - the radius of the dial in pixels.
Multiplier - If the knotmeter has 25 knot settings, the multiplier is 360/25=14.4.
Rotated - How many degrees the start point of the dial is rotated to the left of top dead center. For example, the lowest setting on the Deep Depth meter is rotated 161 degrees to the left.
Max - Was going to be used to check to see if your angle request is out of limits, but I didn't implement it since you probably aren't going to put in those voice commands, so it wasn't really necessary. But they were already defined so I left it in there in case I need to implement it later.
Reversed - By default a dial change uses an angle that travels to the left along the circle. Reversed is set to 1if the travel is actually to the right. Rudder is the only one that travels along the left side, so all the others are reversed.

The following are the gory details of how the dials are actually clicked:

So in the case above, we have the HeadingDial1 (which is the rudder dial) with parent id 701, the center of it at coordinates 804x673 (in 1024x768 mode) and 1700x984 (in 1920x1080 mode). The radius is 46 pixels, the units on the dial are multiplied by 4 (since 1 degree of rudder is 4 real degrees of the dial circle.) It has a maximum value of 38 degrees, and its zero point is rotated 180 degrees left from top dead center. It is not a reversed dial since a positive degree change means to travel left along the dial's circle.

To calculate the pixel to click for a given change, if you say "rudder right 10 degrees" the name of the voice command is actually just 90 with the same ID as this dial (706). It applies various modifiers to the angle based on the rotation and multiplier, and then uses sin() and cos() of the angle (in radians) times the dial radius to determine the pixel offset from the dial center. Got all that? There will be an exam at 4:30 PM, no calculators allowed.