;;------------------------------------------------------------------------------
;;Filename       jimtech.txt
;;System         
;;Author         JIM TAYLOR
;;Date           Wed 3 Sep 1997
;;Description    Technical programming information for Flying Corps
;;------------------------------------------------------------------------------


This file describes programming information for the extra files
provided with Flying corps gold. The following areas are covered:

	Keyboard input

	Analogue interface

	Config file

	Mission editor

Please understand that these programming details are extremely brief.
Quite a bit of additional research will be required by a potential
programmer. I don't know if anybody will ever actually look at this!

Keyboard input
==============

During the 3d sections, the keyboard is mapped into a bit lookup table.
There is a 1 bit per keyboard scancode lookup which describes wether
the key is currently pressed. In addition, each key and shift combination 
can be associated with 2 bits in a larger table. See keytest.h.

When a key is pressed, both bits are set if the key is not already
pressed. When the key is released, only 1 bit is cleared. Within the
program, these bits are polled, and then the second bit is cleared.

In keymaps.h, the macro Keyname() defines equate names for each bit
pair.

The mapping of scancodes and shiftkeys to these equates is now driven
by the binary file FLY\keyboard\keyb3d.bin. By modifying this file, 
the keyboard mapping of the program can be changed.

This file is generated by compiling the table KeyMapping in keymaps.h.

Note that while this system supports 8 shift states only 5 work in
Windows95 (unshifted, shift, alt, ctrl, and 101key), the 3 RHS shift keys
are only distinguishable to the DOS program.

Analogue interface
==================

Another method of getting input from the program is via the analogue
interface. This is a set of DLLs which can be automatically loaded by
the program at runtime via the savegame file.

As well as generating analogue axis inputs which will be recognised by
the game, these routines can also emulate keyboard events and drive
output effects for force feedback joysticks, for instance.

We wish to make this interface public so that new devices can be
supported and further tuning of our interface can take place. 

Sources for the existing drivers has been included.

During calibration, the input routine is called once per screen
refresh, perhaps 5 to 10 times a second. Once the 3d is running it is 
called every 3 centiseconds. We found that the windows driver simply 
stopped the computer if called 33 times a second, so the driver applys 
a stepping down factor.

However, our DOS direct access driver runs far faster even under windows.

Data structure
--------------

The data structures are in the header file analogue.h

There are 32 instances of Axis, and 8 instances of Device.

The enum AxisUses indexes the axis array.
MAKEFIELD() produces a bitfield called AxisUsesField.

The instance of this in each Device indicates which axes the device
will drive. Each device must own at least one axis which is polled by 
the game.

Axis[]:

Note that this structure is 'future proofed' Flying Corps only uses 
7 of the axes for input, and FCG uses 11:
	flight: elevator, aileron, rudder, throttle, cooliehat
	front:	UI_X,UI_Y
	views:	head hdg, pitch, roll; zoom


Axes 28 to 31 are dummies for DLLs which don't actually generate axis 
values. Devices owning one of these axes are always polled, but the 
results are ignored. Instead, these axes are written to by the program 
so that a force feedback device can respond to the effects.

The axis record contains a number of fields which are intended to hold
the calibration information for that axis. It is up to the driver to
make the position field contain a value between -32767, 0, and +32767.
A disabled or ignored axis returns 32768. In addition, the Throttle is
ignored for readings of +/-32767.

The signed values centre, min, and max are particularly useful, and I
also use scale1 and scale2 as multiplication factors. The capitalised
access functions Min(), Max(), etc. access the same word fields as
absolute values.

When used to represent feedback, the access functions FB_Lx() are
intended to represent linear effects, and the functions FB_Rx()
represent rotational effects. When the value is actually changed by 
the game, position will be set non-zero.

In addition, the Axis record contains indexes to the "rowan text
system" for printing calibration messages, and the status field
indicates any errors during calibration.

The equates for these texts are in text.h. A number of spares have been
prepared starting at JOYSTIC_SPARE_2. Alternate text can be poked into
the existing binary text files. See the english\text directory, and look for
"DeviceNameSpare2DeviceNameSpare2" etcetera.

The first 8 entries from JOYSTICK_DEVICE_NAME to JOYSTIC_SPARE_6 are
printed in the driver selection list, and map directly to the order of
the files in the file libcode\dir.dir. All the spares after 6 are
unassigned.

Device[]:

As well as the "activeaxes" field which lists which axes the device
will update and the "rowan text system" device name, which is actually
used to identify the curent driver on the calibration screen, a small ammount of
workspace is provided in the LastButtons field.

The STUBLOADABLE and devfile are used to manage the loading of the
driver, so should not be altered at runtime.

Code interface
==============

Initial loading of the driver code involves making a C++ virtual
function table to access the Device member functions.

In MS-DOS this is done by the main program. It matches the routines
listed in analpub.cpp and creates a new table. The exact order and size
of this list in analpub is critical.

In Windows, the DEVICEHOOK() routine in the DLL provides this facility.

These two interfaces can be used simultaniously, and are: ANALJOY.BIN.

In both cases, a compilation datestamp is checked to ensure compatibility 
between the main program and the DLL. This will have to be modified in
the DLLs to match the date the main program was last compiled.

Unlike the traditional Windows COM, the main program owns the Device
instances, and is able to store useful data in them.

In MS-DOS, the DLL has no access to any data or functions except those
provided as parameters. Any further facilities required must be managed
independently through the DPMI interrupt INT31. Any handles or pointers 
can be held in the LastButtons field.
The 5K low memory data area workspace is passed to all the functions 
(except close()) for use as required. The contents are not guaranteed
between calls, but the address is.
In windows, the DLL has full access to all system functions and data areas
as normal.

The following member functions are required:

Bool	Initialise(UByte*const workspace,Axis	axes[]);
	-allows the DLL to set itself up.

Bool	LoadGame(UByte*const workspace,Axis	axes[]);
	-called after the savegame record has been loaded

Bool	CloseDown(UByte*const workspace);
	-called before the driver is removed from memory

void	PreCalib(UByte*const  workspace,Axis	axes[],axisconfigpages& pages);
	-called on beginning to calibrate the joystick.
		activeaxes has been filled in with required options.
	The routine can initialise any axes it will be calibrating.
	The routine fills in pages. This is a list of 4 axis indexes
	per page displayed during configuration. 
	The normal configuration processes 4 axes at once on 1 page,
	but you can force the display to be repeated up to 5 times.
	If no axes are nabled on the first page then configuration is
	skipped. This can be done if you trust the standard windows joystick
	configuration, or if your device returns fixed values anyway.

Bool	CalibCenter(UByte*const  workspace,Axis	axes[],const AxisUsesField& reqaxes);
Bool	CalibExtreme(UByte*const  workspace,Axis	axes[],const AxisUsesField& reqaxes);
	-These two routines are called continuously during the two
	calibration phases.
	For each axis flagged in reqaxis, the corresponding axes[].position 
	should be filled in with estimates of the joystick position
	in the range -32K to +32K.
	Simultaniously, the values read should be used to calibrate the
	joystick, and the status should be set to indicate the
	progress.
	When the phase is over they simply cease to be called, and
	each axis's status field is checked. There is no "phase over" routine.
	They can cause calibration to cease by setting their return code true,
	indicating that a button on the stick was pressed.


Bool	ReadPosition(UByte*const  workspace,Axis	axes[],const AxisUsesField& reqaxes,keytests* keyboard);
	-This is the main joystick workhorse.
	Position values should be provided for all the reqaxes, but
	others can be filled in as well. Then, when these are requested
	the routine would return quickly.
			
	The keyboard routine gives access to the keyboard class
	instance, and pointers to routines to set and clear keys by
	their rowan key indexes rather than the scancodes.


Additionally, the following prototypes are provided as templates for
writing your routines. You don't have to use them, but I'd advise it:

Bool	ReadHWPos(UByte*const  workspace,Axis	axes[],const AxisUsesField& reqaxes,ULong* newbuttons);
	-gets raw values into the position fields
Bool	ScaleHWPos(UByte*const  workspace,Axis	axes[],const AxisUsesField& reqaxes);
	-converts the raw values into +/-32K
Bool	ReadButtons(UByte*const  workspace,Axis	axes[],const AxisUsesField& reqaxes,ULong* newbuttons,keytests* keyboard);
	-processes the buttons into key presses

Compiling
---------

I compile and link these programs using Watcom C++ ver 10.5. You are
welcome to use whatever you like, but you may have problems with the
optimisation of the analpub.cpp module which is used to interface with
MS-DOS.

Compile the analpub fully optimised and it turns into a series of jmp
statements. Compile the main code module with stack checking turned
off. Link analpub, then the main module, then any libraries you need.

In MS-DOS only code between "ROWANLIB" markers will be loaded in. These
markers are emitted into the code segment. See stub.m for more detail.

The binaries generated live in the LIBCODE directory. They are
referenced by the DIR.DIR file in that directory. 
This is the C type "char [][16];"

There is a lot more information in the various header files.

Config File
===========

The main config file is savegame\settings.cfg and savegame\winset.cfg
for windows.

This file contains 3 elements:

1) A zero terminated date string which is compared with the main
executable's date

2) The structure SaveData (savegame.h) which controls a wide number of
effects

3) The structure Analogue (analogue.h) which contains all 8 devices and
all 32 axes.

In particular, the devices[] contain a word devfile field which identifies the
DLL file to load. If zero that driver is not loaded, but if set to a
certain value, this indicates the location of the DLL. 
The upper byte identifies the directory by accessing the COMMON.DIR
file. LIBCODE is directory 201. The lower byte indexes the DIR.DIR file
inside the directory.

The displayed joystick is Devices[0]
In MS-DOS the mouse input is drive by Devices[1]
In windows the mouse input is handled by winmain.

By directly modifying the savegame file before running the program,
additional drivers can be made to load.


Mission Editor
==============

The mission editor provided with FCG is by no means comprehensive. If
you should decide that you have nothing better to do for a few months,
then you could try to do better.

The basic feature of the mission editor is that it generates a textfile
which is then compiled by the utility MAKEBFW.EXE. However, it reads in
the binary file generated by MAKEBF. There is only ever one user
generated source file in the usersrc directory.

While the mission editor is unable to allow certain advanced features
to be directly edited, these features should be preserved as a
"complex" expression. These expressions are described in bfnumber.h.
Note that while bfnumber is an internal representation of an expression
the structure does not exactly mirror the binary files generated by
MAKEBFW.

MAKEBFW.EXE actually reads bfields\initial.res and then attempts to 
compile bfields\battle.src which contains the source for all the 
battlefields for the original campaigns, but because 
they are unchanged it then generates just one user battlefield.

The actual syntax is:

	MAKEBFW	bfields\initial.res bfields\battle.src

Optionally, "-M" stops uidvals.g from being generated (destroyed), OR "." 
generates the file textfour.err in directory ".".

Textfour.err shows the internal tree structure built by MAKEBFW, and is over
14MB long. Generating it also quadrouples the duration of MAKEBFW.

The basic structure stored to disk is made up of command byte/child
count byte pairs. The command bytes are listed in makebf.h.

The mission editor is able to communicate with the main game via the
scratchpad. It can send "MED: directory file" or "MED: directory".

If the main program doesn't delete this message with a few seconds then
it is launched again.

The mission editor also supports a command-line interface to decompile
one battlefield back into usersrc\custom.bfi:

	meditor	directory\file.bf 

The Family statement in the custom mission is generated by the
following fprint statement:

	fprintf(f,
		"%i %i "
		"%hi %hi "
		"%c %c "
		"%hi %hi %hi "
		"%hi %hi %hi "
		"%hi %hi "
		"%hi %hi "
		"%hi %hi %hi "
		,
		missiondate,timeofday,
		numofsquadronac,numofflightac,
		PlayerNationality+'@',formationtypeindex+'@',
		weathernum,windspeed,winddirection,
		era,region,PlayerAcType,
		PlayerHomeBase,EnemyHomeBase,
		MainObjective,SubObjective,
		WP1,WP2,WP3
		);

The second description is the news.

The main game considers all directories beginning with "M" to be mission
groups, plus "default"; all that begin with "C" are campaigns.

The dictionary system in MAKEBFW is unable to cope with filenames that
start with digits.

In a campaign directory, the dir.dir offers access to the campaign
initialisation data as index 251, and the pilots as 252.

The entries 1..250 form the sequential missions for the campaign.

The campaign structure is in mecamp.h
The pilots are each:

struct PilotType
{
	TextRef	name;			/word filled at runtime
	SkillType	skill;		/byte
	pilotstatus	status;		/byte
	CharacterType	character;	/byte
	MoraleType	morale;		/byte
	NextFollAct	nextfollact;	/byte
	SWord	dayssincerest;
	SWord	kills;
	SWord	daystorecover;
struct	ImageMaps
{
 	UByte	topwing;
 	UByte	tail;
 	UByte	btmwing;
 	UByte	fuselage;
 	UByte	other;
	UByte	emblem;
}	imagemaps;
};

followed by a 50 byte name field.

The mission editor is unable to modify the main world. This is
specified by the region as a rowan file number. The upper byte
specifies the directory number, and the lower byte is the index in
dir.dir.

Additional base worlds could be saved in the campaign directory by
counting backwards from 250. Unfortunately, a directory may only
contain 254 files maximum including dir.dir at position 0.

UniqueIDs
---------

The location of objects is driven by uniqueids. In makebf, an object
can be placed with only a umiqueid band. At runtime such objects will
be allocated uniqueids within that band sequentially. The bands are
listed in uniqueid.h

However, if the object is referenced either within the MAKEBFW system, or
by the main program, then it will be assigned a permanent uniqueID
within it's band by MAKEBFW. MAKEBFW assigns these downward from the
top of the band, but as far as the runtime game is concerned they are
cast in stone in uidvals.g, which MAKEBFW outputs.

The same object can exist in different base era files by applying the
same SetUID instruction in both items.

Needless to say, every object should get a unique uniqeid. If two
objects with the same UID are loaded, or a band overflows the game 
will emit an error message and stop.

This is particularly a problem in events, where complex waypoints
positions often refer to each other forcing MAKEBFW generate a
permanent uid. This event then cannot be loaded twice in the same
mission.

One important feature of MAKEBFW is that it can forward-reference within
the source. However, it sometimes needs help to decide what type a
symbol is. If it guesses wrong then it will generate an error on
reaching the actual definition.

MAKEBFW only generates one error message and then stops. Press a key to
exit.


/Jim

