To make patches which can be used with old save games, KC programs must be compiled with certain options. The code which is added after a previous patch, must be specially marked for the compiler, so that it can produce patch tables which can in turn be used by the runtime system to load an old savegame and correct jump addresses and reuse old loaded strings.
The following keywords are used to mark patches:
#pragma defpatch patchversion patchcompiletime
patchversion is one in a series of integers, starting with 0 for the master version. patchcompiletime is the date of compilation for the patch version, as returned by time(). A compiled story contains its compile time. Also, the symbol _COMPILE_TIME_ is defined at compile time. The save games also contain the compiletime of the story version with which they were saved.
#pragma patch patchversion
... new code goes here ...
#pragma endpatch
Marks a section of new code that was inserted for patch version patchversion.
Patch sections can be nested, but then the inner patch version must be higher than the outer patch version. Code that is present after patch 1 is also present after patch 2 and further patches.
Patch code can always only be added to the last version, never replace existing code. One or several lines of patch code can be inserted at a certain location in the current code, but there are several requirements for the new patch code as well as the existing code.
There are several possible locations where to enter new code easily:
A new class is defined inside the patch section, complete with local functions, variables and code.
#pragma patch 1 class OBJ_DUMMY { int foo() { ... } } #pragma endpatch
Same level as class level, new global functions can be added enclosed in a patch section.
class OBJ_DUMMY #pragma patch 1 int global_foo() { ... } #pragma endpatch
A new class member function can be added inside a class.
class OBJ_DUMMY { int foo() { ... } #pragma patch 1 int foo2() { ... } #pragma endpatch }
The statements may not change the logic of the surrounding code, and they may not contain variable definitions.
class OBJ_DUMMY { int foo() { int a = 3; int b = 4; #pragma patch 1 b = a * b; // Correct use of patch #pragma endpatch a = a + 5; } int foo2() { int a = 3; int b = 4; #pragma patch 2 int c = a * b; // Wrong use, because variable c is defined #pragma endpatch a = a + 5; } int foo3() { int a = 3; int b = 4; if (a == d) #pragma patch 3 c = a * b; // Wrong use, changes the if statement #pragma endpatch a = a + 5; } }
Anything can be done inside the block, including local variable definitions.
class OBJ_DUMMY { int foo() { int a = 3; int b = 4; #pragma patch 1 { int c = a * b; // Correct use of patch a = foo2(c); } #pragma endpatch a = a + 5; } int foo2() { int a = 3; int b = 4; #pragma patch 2 if (a == d) { int c = a * b; // Correct, if-statement is a block a = foo2(c); } #pragma endpatch a = a + 5; } }
There are several type of changes that can be done without #pragma statements:
#define directives can be added since they don't produce extra code
Constants can be changed, if this doesn't change the size of an instruction. Loading int constants produces three type of instructions, the 3 ranges of integers are:
0 <= x <= 31 -> LOADIIQ (1 word, embedded integer constant)
-32768 <= x < 0 or 32 <= x <= 32767 (2 words, 16 bit integer constant)
all other x (3 words, 32 bit integer constant)
If a task was saved while inside piece of code that you want to replace, the task will still run in the old piece of code after the old savegame loaded.
If you can't patch into a piece of code, copy all the code around the problem code, and return/break/continue at the end of the patch code.
Sometimes its easier to just write a new version of a function, then call the new function at the beginning of the old function and return.