
Hi Hackers!

If you are brave enough to try and modify the source, you'll soon realize
that it's in quite a mess, and that's unlikely to change, ever.  The reasons
for that are explained in 0COMPILE.TXT

There are a couple of things that you absolutely have to pay attention to
if you want it to work:

1) Use "var" to declare all variables initialized to something other than
   0 or -1, uvar{b,w,d} for variables initialized to 0 or -1, and "ttdvar"
   to declare all fixed-location variables in TTD's memory
2) Do not specify any segment overrides other than CS or DS without 
   some thought

I'll explain this a bit below.  In addition

3) Be memory efficient rather than speed efficient.
4) When adding stuff, keep to the present format
5) How to add patches


-----------

 1) Use "var" to declare all variables
=======================================

Your assembler code must work in both the DOS and Windows versions, and it
must work no matter at what address it happens to be loaded.

The var macro defines a variable in TTDPatch's memory that makes sure all
of this works correctly.  Use it like this:

	var mynewvariable, dd 55

You can also declare it without an initializer on the same line, if it is 
initialized in the following source line(s):

	var mynewvariable
		db "Hello",0

If you have a variable that needs to be initialized to either 0 or -1, use
the uvar{b,w,d} macros.  These ensure that your variable isn't stored in the
patching code, thus reducing the executable file size.

To make a variable initialized to zero, use

	uvard mydword	// or uvarw, uvarb for bytes, words

You can make it an array by specifying the number of members:

	uvard myarray,256	// 256 DWORDs initialized to 0

And to initialize it to -1, append and s (for "signed"):

	uvard mysigned,1,s	// one DWORD initialized to -1

To declare a variable in TTD's memory, use the macro "ttdvar".  It takes
a parameter that specifies an offset into TTD's data segment.  Note that the
data segment starts at offset 0 for the DOS version, but at a version-specific
offset for each of the Windows versions.  Make sure you get the offset right!

	ttdvar players, 0x52a62

Only a certain range of memory addresses is supported, from 0 to 0x9f1e7.
These are the only variables that have consistent locations in all known
versions of TTD.  For variables outside this range, you must determine the
address by searching for it, instead of hard-coding it.

If you're working on the Windows version and want to find the offset you need
to specify here, take the offset of your variable and subtract the offset of
the "date" variable.  For example, in the American version, subtract 0x458ef0.
If that gives you a value less than 0x7f9e8, you're all set and just use it as
parameter to ttdvar.  However, if it's in the range from 0x9c4e8 to 0x9f27,
you need to adjust it slightly, because the second range is a bit different
in the Windows versions (due to extra sprite info stored in between).  To do
that, simply subtract another 0x90, and you get the value to use as parameter
for ttdvar.  If you value is larger than 0x9f277, you're out of luck and can't
depend on it being constant in all versions.

Finally, you can declare variables relative to the current position or
a code label, using the ovar macro:

	ovar fourbytesago, -4
	ovar startplusten, 10, start
		...
	start:

To make a variable relative to some other variable, just use equ:

	var myvarone, dd 5
	myvartwo equ myvarone+1

After declaring your variable, simply access it as you would normally
using NASM:

	mov esi,mynewvariable	// assign the address
	mov eax,[mynewvariable]	// assign the content


 2) No segment registers
=========================

The DOS and Windows versions have different assignments to the segment
registers FS and GS:


Reg	DOS		Windows
FS	landscape4	reserved
GS	landscape5	reserved

If you find that in the DOS version, a variable is accessed through the
FS or GS registers, DO NOT USE these explicitly in your code.  In the 
Windows version they have a different meaning so it won't work.

Instead, use the landscape4/5 macros:

	mov al,[landscape4(di)]
	mov byte [landscape5(di)],0

The parameter is a 16-bit register that is a valid index in 16-bit 
addressing mode: bx, si, di or bp.  If you need a different register,
or if you need to force the use of a whole 32-bit register for some
reason, use the two-argument form:

	mov al,[landscape4(cx,1)]

This will actually be using the ecx register, because the second argument
is 1.  Note that you need to ensure that ecx is not greater than 0xffff, 
otherwise this instruction will cause a GPF in the DOS version.


 3) Be memory efficient rather than speed efficient.
=====================================================

The whole code in ttdprot.obj has to be kept in memory while TTD runs,
and in DOS this can unnecessarily reduce the memory that is available
to TTD, often leading to crashes on some system.

If you can choose between two instructions, use the one that takes
fewer bytes of code, not the faster one.

If you need data, try to allocate the least amount of memory possible.
Use the uvar* macros to allocate uninitialized memory wherever possible.


 4) When adding stuff, keep to the present format
==================================================

The current format makes adding stuff relatively simple, so try to
keep it in that format.  If that's not possible, try to think of a
different way to accomplish what you need.  The code is messy enough
already, you don't need to make it even more complicated.

For example, use C++ style comments ( // blabla ), to make the .LST file
smaller unless it's really useful to have them there.  In that case use
assembler comments ( ; blabla ).

Set your TAB size to 8.  Even though you may not like it that way,
it's what I use and it should be consistent throughout the code.  So
accept it or don't modify the code - it's up to you.


 5) How to add patches
=======================

OK, so you've found the location of some code in TTD, and have written
a patch.  Now you want to integrate it into TTDPatch.

There are a few steps that you need to take.

(1) What to Patch
*****************

Find the smallest unique sequence of bytes of code that can be
used to find the offset that needs to be patched.  Note that at least
six bytes will have to be changed if you need to call a function, so
make sure that there is enough room in the location that you want to
change.  This sequence does not have to begin or end on an instruction
boundary, you can only include the first few bytes of an instruction if
you like.

Actually, the sequence does not have to be exactly unique, if it is found
a small number of times in memory, that's fine too.  Just remember the total
number of occurences and which one you want to change.

Ideally, you'd then try all ten versions of TTD to see if this strings 
always has the same number of occurences and all versions have to change
the same occurence.  There is often a difference between the DOS and Windows
versions because they seem to have been compiled with include files in a 
different order.  For the most part it's the same in all versions though.

Some hints on how you find a good code sequence:

Things to look for (because they tend to be the same in all versions):
- pure register sequences (mov eax,ebx etc.)
- memory accesses with imm8 offsets (like mov eax,[esi+26h])
- short jmps (conditional or unconditional)

Things to avoid (because they tend to be different in each version):
- far jmps (conditional or unconditional)
- far procedure calls (more than about 200 bytes away)
- absolute variable access outside of TTD's game memory

(2) Tell TTDPatch how to find it
********************************

Edit fragment.ah and add your patch.  It consists of two parts

a) the old code to search for
   Define this using "codefragment old<yourpatchname>"
   Then simply add the instructions or code bytes to search for it.
   If necessary, you can search for a location that's close but not
   exactly at the place you want to patch, and give an offset, e.g.
   "codefragment oldsellengine,16".  This searches for the code given
   in that fragment, adds 16 to the found offset and patches that.

b) the replacement code
   "codefragment new<yourpatchname>"
   and again add the new code.  There will be two special cases:
   - you need to call your own function
     You HAVE to call it using "call runindex(yourfunctionname)". This
     will take up exactly six bytes.
     You also need to add your function to the runindex, by editing the
     runindexlist entries in vars.ah.  Simply add
     "runindexentry yourfunctionname" to the END of the list there.
   - you need to overwrite more code than you replace
     For this, simply use the setfragmentsize macro to NOP out the remainder.

(3) Define a new switch
***********************

You need to add your patch to TTDPatch's switches.
- edit common.h and add a #define for the next unused bit. Also change
  lastbitused and lastbit to reflect this
- add your patch to the end of patchlist in patches.ah
  (the format is explained at the beginning)
- write the code that does the actual patching at the end of patches.ah
  use the macros defined in ttdprot.ah for searching and replacing.
  Don't try to implement your own search, it won't work. Use the
  functions defined at the end of ttdprot.ah, the most straightforward
  one being:
	patchcode old<yourpatch>,new<yourpatch>,occurence,numoccurences

  This will search for the codefragment old<yourpatch>, find the
  appropriate occurence and make sure the total number of occurences
  is right.  Then it will replace the fragment by new<yourpatch>.

  When you do this, you will need to generate the version info again,
  because TTD needs to find the new location and store it.  To do this,
  run TTDPatch once with the "-V" (capital V, not v).  If successful,
  it will write a *.VER file that you then copy to the versions 
  directory and recompile TTD.

(4) Add it to the configuration options
***************************************

You need to make your switch an option in the loading program.  Open
switches.cpp and edit the switches[] array.  Choose an unused letter,
and a name for the .cfg file option.  Then use the YESNO or RANGE macros
to define an on/off or a valued switch.

For a valued switch you need to tell it where in the FLAGDATA structure
you want it to store the code.

You also need to add it to the verbose switch display, and the command
line help text.  Again in switches.cpp, edit the switchorder array and
insert your switch somewhere.  If it has a value, also edit switchvalues.

(5) Add it to the language strings
**********************************

Finally, you also need to add this switch to the language files.  Open
language.h, and add your CFG_ line to the end of the other CFG_ entries
(make sure that it is before LANG_LASTSTRING).

Then, open english.h, and add your CFG_ line.  Add the entry in the 
halflines[] array if your switch is an on/off switch or to 
LANG_FULLSWITCHES if it takes a parameter.  Finally add the two entries 
of the SWITCHTEXT for your new switch.


And *that* is all.

Hey, I didn't say it was easy, did I?
