Patch:Demo

From KeenWiki
Jump to: navigation, search

This page covers patches related to the demo playing in Keen Galaxy, Dreams and Vorticons. The Keen Galaxy code involves the pre-recorded level playthroughs that appear when Keen waits at the Main menu. These are created using special demo chunks in the EGAGRAPH.CKx file that consist of a record of keypresses.

The level is "de-randomized" during demo recording and playback, meaning a demo will always play the same as it is recorded. See also Patch:F10 D Cheat. A special Demo Sign sprite is used when a demo is played back. The -demo switch is used to run the game in 'demo mode'

Keen Dreams lacks demos, though the code to record and play them remains in the executable. Thus Keen Dreams does not currently have its own section. Keen Vorticons also possesses far more limited and nonfunctional code. However working demos have recently been patched so a large section on this page relates to those patches.

Demos are not to be confused with 'demo mode'; a gameplay mode meant to demonstrate the basic features of Keen without allowing access to the full game.


Demos in Keen Galaxy

Two button firing

Two button firing is enabled by default in demos no matter how it is set. If a demo has been recorded without two button firing, then the demo will not play correctly. The following patches disable two button firing in demo playback.

Patch: Disable 2 Button firing in demo
#Keen 4
%patch $8B8B $90 $90 $90 $90 $90 $90 $90
#Keen 5
%patch $8B07 $90 $90 $90 $90 $90 $90 $90
#Keen 6
%patch $8439 $90 $90 $90 $90 $90 $90 $90


Main Menu Demo Loop

The main menu demo loop is the sequence of events that plays out whenever the player selects 'Return to demo' from the main menu. The default sequence is 'terminator text, play demo 1, story screen, demo 2 Highscore demo, demo 3, demo 4' (Then the terminator text will display again, resetting the cycle.

To alter which items play and in what order simply alter the demo pointer list in the section below.

The items for Keen 4 are as follows; $9A $03ED19CARL plays the terminator text, $B8 $000xW $50 $9A $03ED1A31RL $83 $C4 $02 plays demo x, $9A $03ED1D55RL plays the high scores demo and $9A $03ED18B8RL plays the story screen

The items for Keen 5 are as follows; $9A $03ED19C4RL plays the terminator text, $B8 $000xW $50 $9A $03ED1A2BRL $83 $C4 $02 plays demo x, $9A $03ED1D29RL plays the high scores demo and $9A $03ED18B2RL plays the story screen.

The items for Keen 6 are as follows; $9A $03D019A3RL plays the terminator text, $B8 $000xW $50 $9A $03D01A0ARL $83 $C4 $02 plays demo x, $9A $03D01CF9RL plays the high scores demo and $9A $03D01891RL plays the story screen.

The 'after demo stuff' is more complicated involving a number of variables and checks and can be ignored.

Patch: Demo Loop
#Keen 4 demo loop code
%patch $3C18 $83 $3E $7ADEW $00 $74 $07 $9A $03ED19CARL $EB $7B $9A
#Terminator text
$03ED0FCFRL $EB $74 $EB $72 $33 $C0 $50 $9A $03ED1A31RL $83
#Play demo 1
$C4 $02 $EB $65 $9A $03ED18B8RL $EB $5E $B8 $0001W $50 $9A
#Story screen\demo 2
$03ED1A31RL $83 $C4 $02 $EB $50 $9A $03ED1D55RL $EB $49
#Highscores
$B8 $0002W $50 $9A $03ED1A31RL $83 $C4 $02 $EB $3B $33 $FF
#Demo 3
$B8 $0003W $50 $9A $03ED1A31RL $83 $C4 $02 $EB $2B $EB $29
#Demo 4
$9A $05C40D81RL $9A $03ED1D55RL $83 $3E $7A70W $05 $74
#After demo stuff
$18 $83 $3E $7A70W $06 $74 $11 $9A $03ED19CARL $83 $3E $7A70W $05 $74 $05 $83 $3E $7A70W $06 $83 $3E $7A70W $05 $74 $D0 $83 $3E $7A70W $06 $74 $C9 $E9 $FF54W $5F $5E $8B $E5 $5D $CB
#Keen 5 demo loop code
%patch $3C12 $83 $3E $6FDEW $00 $74 $07 $9A $03ED19C4RL $EB $7B $9A
#Terminator text
$03ED0FC9RL $EB $74 $EB $72 $33 $C0 $50 $9A $03ED1A2BRL $83
#Play demo 1
$C4 $02 $EB $65 $9A $03ED18B2RL $EB $5E $B8 $0001W $50 $9A
#Story screen\demo 2
$03ED1A2BRL $83 $C4 $02 $EB $50 $9A $03ED1D29RL $EB $49
#Highscores
$B8 $0002W $50 $9A $03ED1A2BRL $83 $C4 $02 $EB $3B $33 $FF
#Demo 3
$B8 $0003W $50 $9A $03ED1A2BRL $83 $C4 $02 $EB $2B $EB $29
#Demo 4
$9A $05C10D6CRL $9A $03ED1D29RL $83 $3E $6F70W $05 $74
#After demo stuff
$18 $83 $3E $6F70W $06 $74 $11 $9A $03ED19C4RL $83 $3E $6F70W $05 $74 $05 $83 $3E $6F70W $06 $83 $3E $6F70W $05 $74 $D0 $83 $3E $6F70W $06 $74 $C9 $E9 $FF54W $5F $5E $8B $E5 $5D $CB
#Keen 6 demo loop code
%patch $3BF9 $83 $3E $761AW $00 $74 $07 $9A $03D019A3RL $EB $7B $9A
#Terminator text
$03D00FBARL $EB $74 $EB $72 $33 $C0 $50 $9A $03D01A0ARL $83
#Play demo 1
$C4 $02 $EB $65 $9A $03D01891RL $EB $5E $B8 $0001W $50 $9A
#Story screen\demo 2
$03D01A0ARL $83 $C4 $02 $EB $50 $9A $03D01CF9RL $EB $49
#Highscores
$B8 $0002W $50 $9A $03D01A0ARL $83 $C4 $02 $EB $3B $33 $FF
#Demo 3
$B8 $0003W $50 $9A $03D01A0ARL $83 $C4 $02 $EB $2B $EB $29
#Demo 4
$9A $05A10D81RL $9A $03D01CF9RL $83 $3E $75ACW $05 $74
#After demo stuff
$18 $83 $3E $75ACW $06 $74 $11 $9A $03D019A3RL $83 $3E $75ACW $05 $74 $05 $83 $3E $75ACW $06 $83 $3E $75ACW $05 $74 $D0 $83 $3E $75ACW $06 $74 $C9 $E9 $FF54W $5F $5E $8B $E5 $5D $CB


Demo loop list

This is the Main menu demo list, a list of various things the game does at the main menu. By simply swapping various pointers around it is possible to change the order that various actions appear or replace one action with another. A novel option is to replace the terminator text with the star wars story. Notice that all these pointers refer to the code in the section above.

The values $04D1W, $04CBW or $04D2W (Keen 4, Keen 5 or Keen 6 respectively) is 'nothing' and causes an option to be skipped. All but the first option can be replaced by this. (If the first option is replaced by this the game enters an infinite loop.)

Patch: Keen 4
#List length
%patch $3C0B $06
#List location
%patch $3C16 $04E9W
#$3CB9
#Main menu actions list
%patch $3CB9 $0448W
#3C18: Terminator sequence and title screen
%patch $3CBB $045FW
#3C2F: Demo 1
%patch $3CBD $046CW
#3C3C: Star wars story
%patch $3CBF $0473W
#3C43: Demo 2
%patch $3CC1 $0481W
#3C51: High score demo
%patch $3CC3 $0488W
#3C58: Demo 3
%patch $3CC5 $0496W
#3C66: Demo 4

Patch: Keen 5
#List length
%patch $3C05 $06
#List location
%patch $3C10 $04E3W
#$3CB9
#Main menu actions list
%patch $3CB3 $0442W
#3C12: Terminator sequence and title screen
%patch $3CB5 $0459W
#3C29: Demo 1
%patch $3CB7 $0466W
#3C36: Star wars story
%patch $3CB9 $046DW
#3C3D: Demo 2
%patch $3CBB $047BW
#3C4B: High score demo
%patch $3CBD $0482W
#3C52: Demo 3
%patch $3CBF $0490W
#3C60: Demo 4

Patch: Keen 6
#List length
%patch $3BEC $06
#List location
%patch $3BF7 $04EAW
#$3C9A
#Main menu actions list
%patch $3C9A $0449W
#Terminator sequence and title screen (At $3BF9)
%patch $3C9C $0460W
#Demo 1 (At $3C10)
%patch $3C9E $046DW
#Star wars story (At $3C1D)
%patch $3CA0 $0474W
#Demo 2 (At $3C24)
%patch $3CA2 $0482W
#High score demo (At $3C32)
%patch $3CA4 $0489W
#Demo 3 (At $3C39)
%patch $3CA6 $0497W
#Demo 4 (At $3C47)


Play in demo levels

This patch allows the player to play the demo levels. When the 'game' is ended, the level is exited or Keen dies the player will be returned to the main menu. This in effect allows him to 'practice' the level. It also allows Keen to play in levels he may not be able to access from the map.

Some bugs can occur, for example, by default if Keen presses enter in the high scores demo the game will freeze (Because the high scores has no level name.)

Patch: Let Keen play the demo levels
#Keen 4
%patch $599A $90 $90 $90 $90 $90


Disable Demos

The first set of patches stop demos from being played at all; the starting terminator text and the star wars story screen appear, but no level demos are shown. This also disables the high score screen. (Though new scores can be added if high enough, Keen playing the score level will not be shown.)

The second set of patches disable the demos one by one; for example, in Keen 4, using the first line will stop the first demo (In Keen 4 Slug Village by default) from playing (And will instead play the star wars story screen.) With all four patches used all but the high score demo will be stopped.

Patch: Disable all demos
#Disable all demos -Keen 4
%patch $5901 $CB
#Disable all demos -Keen 5
%patch $58FB $CB
#Disable all demos -Keen 6
%patch $570A $CB

Patch: Disable single demo
#Disable single demos -Keen 4
%patch $3C32 $90 $90 $90 $90 $90
#Demo 1
%patch $3C47 $90 $90 $90 $90 $90
#Demo 2
%patch $3C5C $90 $90 $90 $90 $90
#Demo 3
%patch $3C6C $90 $90 $90 $90 $90
#Demo 4
#Disable single demos -Keen 5
%patch $3C2C $90 $90 $90 $90 $90
#Demo 1
%patch $3C41 $90 $90 $90 $90 $90
#Demo 2
%patch $3C56 $90 $90 $90 $90 $90
#Demo 3
%patch $3C66 $90 $90 $90 $90 $90
#Demo 4
#Disable single demos -Keen 6
%patch $3C13 $90 $90 $90 $90 $90
#Demo 1
%patch $3C28 $90 $90 $90 $90 $90
#Demo 2
%patch $3C3D $90 $90 $90 $90 $90
#Demo 3
%patch $3C4D $90 $90 $90 $90 $90
#Demo 4


Don't play highscroe demo in demo loop

This patch stops the Highscore demo being shown in the main menu demo loop. All the ordinary demos will play, but not the special one showing the high scores. The demo will play when Keen gets a new high score, or quits his game however.

Patch: Don't play highscroe demo in demo loop
#Don't play high score demo in demo loop - Keen 4
%patch $3C51 $90 $90 $90 $90 $90
#Don't play high score demo in demo loop - Keen 5
%patch $3C4B $90 $90 $90 $90 $90
#Don't play high score demo in demo loop - Keen 6
%patch $3C32 $90 $90 $90 $90 $90


Don't play Highscore demo when a game is quit or new high score is obtained

Separate from the above patch this prevents the Highscore demo being played when a game is quit. Instead the player will be sent to the title screen.

Patch: Don't play Highscore demo when a game is quit or new high score is obtained
#Don't play Highscore demo when a game is quit or new high score is obtained - Keen 4
%patch $3C7D $90 $90 $90 $90 $90
#Don't play Highscore demo when a game is quit or new high score is obtained - Keen 5
%patch $3C77 $90 $90 $90 $90 $90
#Don't play Highscore demo when a game is quit or new high score is obtained - Keen 6
%patch $3C5E $90 $90 $90 $90 $90


Demo numbers

The demo chunks are the graphics chunks in EGAGRAPH.CKx used for demo files. The game contains a variable that tracks which chunk is the start of the demo chunks. There are also four calls to the 'run demo' code that have different values, three of which can be edited (The fourth doesn't have space.)

It is thus possible to reduce the number of different demos (But not increase that number.) by making two or more values the same. If the patcher wishes a demo to be 0, they must replace the three byte string '$B8 $000x' with '$90 $33C0W'

Note that this does not affect demo #4, the high score demo. (See above section.)

Patch: Start of demo chunks
#Keen 4
%patch $5913 $128AW

Patch: Demo numbers
#Keen 4
%patch $3C2F $33C0W %patch $3C43 $B8 $0001W %patch $3C58 $B8 $0002W %patch $3C68 $B8 $0003W
#Keen 5
%patch $3C29 $33C0W %patch $3C3D $B8 $0001W %patch $3C52 $B8 $0002W %patch $3C62 $B8 $0003W
#Keen 6
%patch $3C10 $33C0W %patch $3C24 $B8 $0001W %patch $3C39 $B8 $0002W %patch $3C49 $B8 $0003W


Highscore demo number

Related to the above section, the Highscore demo number is the demo used for the Highscores. In order not to mess up, it must stay in the same screen at the top leftmost corner of the level. By default it is the highest demo number possible.

Patch: Highscore demo number
#Keen 4
%patch $5C34 $0004W
#Keen 5
%patch $5C08 $0004W
#Keen 6
%patch $5A08 $0004W


Difficulty of demos

This patch alters the default difficulty level of demos both when recording and playing them. By default all demos are recorded\played on normal difficulty mode. Making this something aside from the difficulty will result in a difficulty level of 0, which can have some side effects. (It can also make the demo levels drastically different from actual gameplay.) See also: Patch:Game stats.

Note that if a demo is played back on a different difficulty level than it was recorded the demo may degrade and do unexpected things.

Patch: Keen 4
#Demo difficulty level
%patch $61EC $7A5CW $0002W


Demo errors

There are three errors that can crash the game that involve demos.


Demo loop exited error

This error occurs when something goes wrong with the demo loop in Keen Galaxy or Dreams. The exact sources of error vary but are usually a corrupt demo file or a problem with the demo loop code. Checking demo files and demo patches is a good way to debug this.

Patch: Demo loop exited error
#Keen 4:
%patch $3ECF $01DAW
#Text called from
%patch $2F04A "Demo loop exited???" $00
#Keen 5:
%patch $3EC9 $018AW
#Text called from
%patch $304CA "Demo loop exited???" $00
#Keen 6:
%patch $3CF8 $011EW
#Text called from
%patch $30E4E "Demo loop exited???" $00
#Keen 7:
%patch $3BF7 $0357W
#Text called from
%patch $23DC7 "Demo loop exited???" $00


Demo buffer overflow error

This error occurs when something goes wrong with the demo cheat in Keen Galaxy or Dreams. Specifically it occurs when a demo is recorded for too long, exceeding the amount of memory available for storing a demo file. The solution is to stop recording a demo before this happens. If this does not occur during demo recording then it is due to a random patch corrupting the game.

Patch: Demo buffer overflow error
#Keen 4:
%patch $152FA $3EFCW
#Text called from
%patch $32D6C "Demo buffer overflow" $00
#Keen 5:
%patch $16297 $35B4W
#Text called from
%patch $338F4 "Demo buffer overflow" $00
#Keen 6:
%patch $14E2B $38FCW
#Text called from
%patch $3462C "Demo buffer overflow" $00
#Keen 7:
%patch $1093A $461EW
#Text called from
%patch $2808E "Demo buffer overflow" $00


Demo playback exceeded

This error occurs when trying to play a demo that is too large for the memory set aside. It is thus similar to the above error except that it occurs when a demo is being played rather than recorded. This should not be possible however if a demo chunk is corrupted or was recorded under different memory conditions than the demo is being played back then it is possible that this error will occur. The only solution is to use a smaller\shorter demo chunk for this demo. If this error occurs no matter what demo is used or outside of the demo loop then it is due to a random patch corrupting the game.

Patch: Demo playback exceeded
#Keen 4:
%patch $1504B $3EE5W
#Text called from
%patch $32D55 "Demo playback exceeded" $00
#Keen 5:
%patch $15FE8 $359DW
#Text called from
%patch $338DD "Demo playback exceeded" $00
#Keen 6:
%patch $14B7C $38E5W
#Text called from
%patch $34615 "Demo playback exceeded" $00
#Keen 7:
%patch $106E2 $4607W
#Text called from
%patch $28077 "Demo playback exceeded" $00


Demos in Keen Vorticons

In Keen Vorticons there is a stunted section of code that loads and saves a demo file. If run this will freeze the game (load) or write a jumble of memory to a file (save.) A patch that takes advantage of this code follows, overwriting the function handling restoration of saved games in the way.


Demo recording/playback functionality

By giving up the ability to restore saved games and modifying some code sections, menu loop included, it is possible to add the ability to record and playback demos.

The "Continue Game" menu item is replaced with "Record A Demo". Furthermore, the menu loop shows the title for a shorter while (about one second by default) and then proceeds to demo playback. The patch should take care of various causes of demo playback inconsistencies (such as the pseudo-random number generator). There are chances that some kinds of inconsistencies have not yet been found.

For more details about the asm files mentioned in the patches, see the original asm source files below made for Keen 1. It should be noted that a few additional modifications to the asm files are required for Keen 3, in comparison to the changes needed for Keen 2.

Patch: Keen 1 v1.31, Apogee Software release
### Modify sync_drawing code for proper handling of demo recording/playback
%patch 0x25F $A1 $563EW $2B $06 $5135W $3C $06 $72 $F5 $B8 $0006W $99 $A3 $5B14W $01 $06 $5135W $11 $16 $5137W
#Let us also replace ticks with ticks_sync within int_8_handler
%patch 0xBFAD $563EW
# Add 1 to ticks_sync (lo)
%patch 0xBFB2 $5640W
# Add carry to ticks_sync+2
#Finally, modify draw_level at the very beginning
%patch 0x4BA3 $C7 $46 $FE $00 $00 $C7 $06 $4C $60 $01 $00 $31 $C0 $A3 $56 $82 $A3 $AA $5D $A3 $60 $6C $FF $76 $04 $E8 $2C $CA $44 $44 $A1 $E0 $6E $8B $16 $DE $6E $81 $C2 $00 $60 $83 $D0 $FF $89 $16 $D0 $6E $A3 $D2 $6E $A1 $E4 $6E $8B $16 $E2 $6E $81 $C2 $00 $B0 $83 $D0 $FF $89 $16 $D4 $6E $A3 $D6 $6E $A1 $D2 $6E $8B $16 $D0 $6E $3B $06 $4C $56 $7F $16 $7C $06 $3B $16 $4A $56 $73 $0E $A1 $4C $56 $8B $16 $4A $56 $89 $16 $D0 $6E $A3 $D2 $6E $A1 $D6 $6E $8B $16 $D4 $6E $3B $06 $50 $56 $7F $16 $7C $06 $3B $16 $4E $56 $73 $0E %patch 0x4C23 $A1 $50 $56 $8B $16 $4E $56 $89 $16 $D4 $6E $A3 $D6 $6E $A1 $D2 $6E $8B $16 $D0 $6E $3B $06 $C0 $7F $7C $16 $7F $06 $3B $16 $BE $7F $76 $0E $A1 $C0 $7F $8B $16 $BE $7F $89 $16 $D0 $6E $A3 $D2 $6E $A1 $D6 $6E $8B $16 $D4 $6E $3B $06 $C4 $7F $7C $16 $7F $06 $3B $16 $C2 $7F $76 $0E $A1 $C4 $7F $8B $16 $C2 $7F $89 $16 $D4 $6E $A3 $D6 $6E $E8 $BF $B5 $E8 $10 $20 $C7 $06 $1E $82 $01 $00 $E8 $B0 $6A $E8 $00 $B6 $E8 $FD $B5 $E8 $BA $1F $8B $16 $D2 $6E $A1 $D0 $6E $B1 $0C $E8 $D7 $94 $A3 $56 $56 $8B $16 $D6 $6E $A1 $D4 $6E $B1 $0C $E8 $C8 $94 $A3 $12 $5B $31 $C0 $A3 $3E $56 $A3 $40 $56 $A3 $35 $51 $A3 $37 $51 $B0 $0F $A3 $14 $5B $EB $03
#Modify demo code in handle_ctrl, so it IS checked if end of file has arrived
#(with an end-of-demo 16-bit pointer being stored in keen_gp.unknown for now...)
%patch 0x5A39 $55 $89 $E5 $83 $EC $0E $83 $3E $32 $97 $00 $74 $0A $83 $3E $32 $97 $02 $74 $03 $E9 $89 $00 $8B $5E $08 $D1 $E3 $8B $9F $3C $83 $83 $FB $03 $77 $4A $D1 $E3 $2E $FF $A7 $23 $5B $8D $46 $FA $16 $50 $16 $8D $46 $F2 $50 $E8 $31 $FB $83 $C4 $04 $EB $26 $8D $46 $FA $16 $50 $16 $8D $46 $F2 $50 $E8 $57 $FC $83 $C4 $04 $EB $14 $8D $46 $FA $16 $50 $D1 $EB $4B $53 $16 $8D $46 $F2 $50 $E8 $15 $FE $83 $C4 $06 $16 $8D $46 $F2 $50 $B9 $06 $00 $E8 $F3 $87 $83 $3E $32 $97 $02 $75 $5C $8B $46 $FA $C1 $E0 $02 $8B $56 $FE $D1 %patch 0x5AB9 $E2 $09 $D0 $0B $46 $FC $8B $1E $62 $83 $81 $FB $F9 $96 $75 $08 $68 $5E $32 $E8 $92 $B7 $44 $44 $88 $07 $FF $06 $62 $83 $EB $32 $8B $1E $62 $83 $3B $1E $73 $32 $75 $08 $C7 $06 $BC $6E $02 $00 $EB $20 $8A $07 $98 $89 $C2 $FF $06 $62 $83 $24 $01 $89 $46 $FC $88 $D0 $24 $02 $D0 $F8 $89 $46 $FE $80 $E2 $3C $C0 $FA $02 $89 $56 $FA $FF $76 $06 $FF $76 $04 $8D $46 $FA $16 $50 $B9 $06 $00 $E8 $7F $87 $8B $46 $04 $89 $EC $5D $C3 $65 $5A $77 $5A $89 $5A $89 $5A
#Replace continue_game with demo recording handling, along with an internal
#function that reset some things *after* demo recording (like ammo count).
%patch 0x9C4E $55 $89 $E5 $83 $EC $05 $16 $6A $01 $6A $1A $E8 $62 $C4 $83 $C4 $04 $68 $30 $32 $E8 $6D $C6 $44 $44 $E8 $D0 $BF $6A $02 $8D $46 $FD $50 $E8 $44 $C9 $83 $C4 $04 $09 $C0 $0F $84 $89 $00 $8D $46 $FD $50 $E8 $5F $32 $44 $44 $A3 $04 $83 $E8 $B2 $BE $C7 $06 $57 $51 $00 $00 $6A $00 $E8 $7D $23 $44 $44 $6A $06 $6A $00 $68 $D0 $7F $E8 $97 $37 $83 $C4 $06 $E8 $63 $00 $C7 $06 $BC $6E $00 $00 $FF $36 $04 $83 $E8 $B4 $AE $44 $44 $E8 $8F $CF $E8 $7D $BF $6A $01 $6A $17 $E8 $FA $C3 $83 $C4 $04 $68 $48 $32 $E8 $05 $C6 $44 %patch 0x9CCE $44 $E8 $96 $C5 $25 $FF $00 $3C $30 $7C $0D $3C $39 $7F $09 $83 $C0 $D0 $50 $E8 $E5 $BE $44 $44 $C7 $06 $32 $97 $00 $00 $E8 $9E $CF $C7 $06 $D2 $6E $00 $00 $C7 $06 $D0 $6E $00 $10 $6A $5A $E8 $6B $69 $44 $44 $E8 $27 $00 $31 $C0 $89 $EC $5D $C3 $55 $89 $E5 $31 $C0 $A3 $CA $AA $A3 $CC $AA $A3 $C2 $6E $A3 $C4 $6E $A3 $DE $6E $A3 $E0 $6E $A3 $E2 $6E $A3 $E4 $6E $89 $EC $5D $C3 $55 $89 $E5 $C7 $06 $8E $AA $00 $00 $6A $5A $E8 $31 $69 $44 $44 $C7 $06 $C6 $AA $04 $00 $C7 $06 $C8 $AA $00 $00 $6A $32 $6A $00 $68 $94 $AA $E8 $E7 $36 $83 $C4 $06 $C7 $06 $9A $AA $00 $00 $6A $10 $6A $00 $68 $DE $AA $E8 $D4 $36 $83 $C4 $06 $89 $EC $5D $C3
### Let's begin modifying do_intro_and_menu. We want ###
### to shorten the time needed to wait for the demos. ###
%patch 0x90D5 $0092W
#Demo playback patch, modifying most of do_intro_and_menu
%patch 0x912A $74 $03 $E8 $A4 $06 $2B $3E $14 $5B $09 $FF $0F $8F $F6 $00 $E9 $A3 $00 $68 $81 $27 $68 $90 $82 $E8 $0E $46 $83 $C4 $04 $6A $0A $8D $46 $EA $50 $56 $E8 $38 $42 $83 $C4 $06 $8D $46 $EA $50 $68 $90 $82 $E8 $BB $45 $83 $C4 $04 $68 $86 $27 $68 $90 $82 $E8 $AF $45 $83 $C4 $04 $FF $36 $06 $25 $68 $90 $82 $E8 $A2 $45 $83 $C4 $04 $68 $00 $80 $68 $90 $82 $E8 $1A $43 $83 $C4 $04 $83 $F8 $FF $74 $52 $89 $C7 $50 $E8 $CA $3F $44 $44 $05 $70 $83 $A3 $73 $32 $57 $E8 $0D $34 $44 $44 $56 $E8 $B0 $C9 $44 $44 $31 $FF $89 $3E %patch 0x91AA $BC $6E $89 $3E $57 $51 $57 $E8 $5F $2E $44 $44 $6A $06 $57 $68 $D0 $7F $E8 $7A $42 $83 $C4 $06 $E8 $46 $0B $FF $36 $04 $83 $E8 $9D $B9 $44 $44 $C7 $06 $32 $97 $00 $00 $E8 $55 $0B $83 $3E $BC $6E $01 $74 $08 $46 $83 $FE $0A $0F $8C $56 $FF $31 $F6 $E8 $A2 $DA $6A $5A $E8 $7B $74 $44 $44 $BF $92 $00 $E8 $41 $70 $E8 $8E $70 $C7 $06 $D0 $6E $00 $20 $C7 $06 $D2 $6E $00 $00 $C7 $06 $D4 $6E $00 $20 $C7 $06 $D6 $6E $00 $00 $E8 $20 $25 $E8 $BA $05 $E8 $2D $DA $83 $3E $BC $6E $01 $0F $84 $2D $00 $E9 $D8 $FE
#Replace text "Continue Game" with "Record A Demo"
%patch 0x15EF3 " Record A Demo" $0A $00
#A couple of more text replacements for the continue_game function
#(now used for demo recording)
%patch 0x16280 "Choose a level (1-99): " $00 %patch 0x16298 "Save as demo
#(0-9): " $00
#Also for this
%patch 0x162AE "Demo buffer overflow" $00

Patch: Keen 2 v1.31, Apogee Software release
### Modify sync_drawing code for proper handling of demo recording/playback
%patch 0x25F $A1 $561AW $2B $06 $5111W $3C $06 $72 $F5 $B8 $0006W $99 $A3 $5AF0W $01 $06 $5111W $11 $16 $5113W
#Let us also replace ticks with ticks_sync within int_8_handler
%patch 0xB6C5 $561AW
# Add 1 to ticks_sync (lo)
%patch 0xB6CA $561CW
# Add carry to ticks_sync+2
#Finally, modify draw_level at the very beginning
%patch 0x7907 $C7 $46 $FE $00 $00 $C7 $06 $28 $60 $01 $00 $31 $C0 $A3 $F6 $96 $A3 $86 $5D $A3 $3C $6C $FF $76 $04 $E8 $E0 $C1 $44 $44 $A1 $BC $6E $8B $16 $BA $6E $81 $C2 $00 $60 $83 $D0 $FF $89 $16 $AC $6E $A3 $AE $6E $A1 $C0 $6E $8B $16 $BE $6E $81 $C2 $00 $B0 $83 $D0 $FF $89 $16 $B0 $6E $A3 $B2 $6E $A1 $AE $6E $8B $16 $AC $6E $3B $06 $28 $56 $7F $16 $7C $06 $3B $16 $26 $56 $73 $0E $A1 $28 $56 $8B $16 $26 $56 $89 $16 $AC $6E $A3 $AE $6E $A1 $B2 $6E $8B $16 $B0 $6E $3B $06 $2C $56 $7F $16 $7C $06 $3B $16 $2A $56 $73 $0E %patch 0x7987 $A1 $2C $56 $8B $16 $2A $56 $89 $16 $B0 $6E $A3 $B2 $6E $A1 $AE $6E $8B $16 $AC $6E $3B $06 $9C $7F $7C $16 $7F $06 $3B $16 $9A $7F $76 $0E $A1 $9C $7F $8B $16 $9A $7F $89 $16 $AC $6E $A3 $AE $6E $A1 $B2 $6E $8B $16 $B0 $6E $3B $06 $A0 $7F $7C $16 $7F $06 $3B $16 $9E $7F $76 $0E $A1 $A0 $7F $8B $16 $9E $7F $89 $16 $B0 $6E $A3 $B2 $6E $E8 $5B $88 $E8 $BA $B4 $C7 $06 $BE $96 $01 $00 $E8 $64 $34 $E8 $9C $88 $E8 $99 $88 $E8 $64 $B4 $8B $16 $AE $6E $A1 $AC $6E $B1 $0C $E8 $A3 $5E $A3 $32 $56 $8B $16 $B2 $6E $A1 $B0 $6E $B1 $0C $E8 $94 $5E $A3 $EE $5A $31 $C0 $A3 $1A $56 $A3 $1C $56 $A3 $11 $51 $A3 $13 $51 $B0 $0F $A3 $F0 $5A $EB $03
#Modify demo code in handle_ctrl, so it IS checked if end of file has arrived
#(with an end-of-demo 16-bit pointer being stored in keen_gp.unknown for now...)
%patch 0x200C $55 $89 $E5 $83 $EC $0E $83 $3E $5C $94 $00 $74 $0A $83 $3E $5C $94 $02 $74 $03 $E9 $89 $00 $8B $5E $08 $D1 $E3 $8B $9F $6E $80 $83 $FB $03 $77 $4A $D1 $E3 $2E $FF $A7 $F6 $20 $8D $46 $FA $16 $50 $16 $8D $46 $F2 $50 $E8 $31 $FB $83 $C4 $04 $EB $26 $8D $46 $FA $16 $50 $16 $8D $46 $F2 $50 $E8 $57 $FC $83 $C4 $04 $EB $14 $8D $46 $FA $16 $50 $D1 $EB $4B $53 $16 $8D $46 $F2 $50 $E8 $15 $FE $83 $C4 $06 $16 $8D $46 $F2 $50 $B9 $06 $00 $E8 $2F $B9 $83 $3E $5C $94 $02 $75 $5C $8B $46 $FA $C1 $E0 $02 $8B $56 $FE $D1 %patch 0x208C $E2 $09 $D0 $0B $46 $FC $8B $1E $8E $80 $81 $FB $25 $94 $75 $08 $68 $DC $32 $E8 $79 $F1 $44 $44 $88 $07 $FF $06 $8E $80 $EB $32 $8B $1E $8E $80 $3B $1E $F1 $32 $75 $08 $C7 $06 $98 $6E $02 $00 $EB $20 $8A $07 $98 $89 $C2 $FF $06 $8E $80 $24 $01 $89 $46 $FC $88 $D0 $24 $02 $D0 $F8 $89 $46 $FE $80 $E2 $3C $C0 $FA $02 $89 $56 $FA $FF $76 $06 $FF $76 $04 $8D $46 $FA $16 $50 $B9 $06 $00 $E8 $BB $B8 $8B $46 $04 $89 $EC $5D $C3 $38 $20 $4A $20 $5C $20 $5C $20
#Replace continue_game with demo recording handling, along with an internal
#function that reset some things *after* demo recording (like ammo count).
%patch 0x962B $55 $89 $E5 $83 $EC $05 $16 $6A $01 $6A $1A $E8 $58 $90 $83 $C4 $04 $68 $AE $32 $E8 $63 $92 $44 $44 $E8 $C6 $8B $6A $02 $8D $46 $FD $50 $E8 $3A $95 $83 $C4 $04 $09 $C0 $0F $84 $89 $00 $8D $46 $FD $50 $E8 $2A $2F $44 $44 $A3 $3C $80 $E8 $A8 $8A $C7 $06 $33 $51 $00 $00 $6A $00 $E8 $B8 $20 $44 $44 $6A $06 $6A $00 $68 $70 $94 $E8 $F5 $34 $83 $C4 $06 $E8 $63 $00 $C7 $06 $98 $6E $00 $00 $FF $36 $3C $80 $E8 $3B $E2 $44 $44 $E8 $C0 $97 $E8 $73 $8B $6A $01 $6A $17 $E8 $F0 $8F $83 $C4 $04 $68 $C6 $32 $E8 $FB $91 $44 %patch 0x96AB $44 $E8 $8C $91 $25 $FF $00 $3C $30 $7C $0D $3C $39 $7F $09 $83 $C0 $D0 $50 $E8 $DB $8A $44 $44 $C7 $06 $5C $94 $00 $00 $E8 $CF $97 $C7 $06 $AE $6E $00 $00 $C7 $06 $AC $6E $00 $10 $6A $5A $E8 $8E $6F $44 $44 $E8 $27 $00 $31 $C0 $89 $EC $5D $C3 $55 $89 $E5 $31 $C0 $A3 $E0 $9A $A3 $E2 $9A $A3 $9E $6E $A3 $A0 $6E $A3 $BA $6E $A3 $BC $6E $A3 $BE $6E $A3 $C0 $6E $89 $EC $5D $C3 $55 $89 $E5 $C7 $06 $A4 $9A $00 $00 $6A $5A $E8 $54 $6F $44 $44 $C7 $06 $DC $9A $04 $00 $C7 $06 $DE $9A $03 $00 $6A $32 $6A $00 $68 $AA $9A $E8 $45 $34 $83 $C4 $06 $C7 $06 $B0 $9A $01 $00 $6A $10 $6A $00 $68 $F4 $9A $E8 $32 $34 $83 $C4 $06 $89 $EC $5D $C3
### Let's begin modifying do_intro_and_menu. We want ###
### to shorten the time needed to wait for the demos. ###
%patch 0x8B69 $0092W
#Demo playback patch, modifying most of do_intro_and_menu
%patch 0x8BBE $74 $03 $E8 $91 $06 $2B $3E $F0 $5A $09 $FF $0F $8F $F6 $00 $E9 $A3 $00 $68 $E1 $26 $68 $D4 $7F $E8 $B5 $42 $83 $C4 $04 $6A $0A $8D $46 $EA $50 $56 $E8 $DF $3E $83 $C4 $06 $8D $46 $EA $50 $68 $D4 $7F $E8 $62 $42 $83 $C4 $04 $68 $E6 $26 $68 $D4 $7F $E8 $56 $42 $83 $C4 $04 $FF $36 $66 $24 $68 $D4 $7F $E8 $49 $42 $83 $C4 $04 $68 $00 $80 $68 $D4 $7F $E8 $C1 $3F $83 $C4 $04 $83 $F8 $FF $74 $52 $89 $C7 $50 $E8 $71 $3C $44 $44 $05 $9C $80 $A3 $F1 $32 $57 $E8 $91 $30 $44 $44 $56 $E8 $EF $94 $44 $44 $31 $FF $89 $3E %patch 0x8C3E $98 $6E $89 $3E $33 $51 $57 $E8 $E3 $2A $44 $44 $6A $06 $57 $68 $70 $94 $E8 $21 $3F $83 $C4 $06 $E8 $8F $0A $FF $36 $3C $80 $E8 $6D $EC $44 $44 $C7 $06 $5C $94 $00 $00 $E8 $9E $0A $83 $3E $98 $6E $01 $74 $08 $46 $83 $FE $0A $0F $8C $56 $FF $31 $F6 $E8 $1C $A2 $6A $5A $E8 $E7 $79 $44 $44 $BF $92 $00 $E8 $AD $75 $E8 $FA $75 $C7 $06 $AC $6E $00 $20 $C7 $06 $AE $6E $00 $00 $C7 $06 $B0 $6E $00 $20 $C7 $06 $B2 $6E $00 $00 $E8 $A4 $21 $E8 $A7 $05 $E8 $A7 $A1 $83 $3E $98 $6E $01 $0F $84 $2D $00 $E9 $D8 $FE
#Replace text "Continue Game" with "Record A Demo"
%patch 0x1A6C6 " Record A Demo" $0A $00
#A couple of more text replacements for the continue_game function
#(now used for demo recording)
%patch 0x1AA2E "Choose a level (1-99): " $00 %patch 0x1AA46 "Save as demo
#(0-9): " $00
#Also for this
%patch 0x1AA5C "Demo buffer overflow" $00

Patch: Keen 3 v1.31, Apogee Software release
### Modify sync_drawing code for proper handling of demo recording/playback
%patch 0x25F $A1 $586AW $3C $06 $72 $F5 $B8 $0006W $99 $A3 $5D40W $01 $06 $5361W $11 $16 $5363W $90 $90 $90 $90
#Let us also replace ticks with ticks_sync within int_8_handler
%patch 0xCA23 $586AW
# Add 1 to ticks_sync (lo)
%patch 0xCA28 $586CW
# Add carry to ticks_sync+2
#Finally, modify draw_level at the very beginning so
%patch 0x81AC $C7 $46 $FE $00 $00 $C7 $06 $78 $62 $01 $00 $31 $C0 $A3 $46 $99 $A3 $D6 $5F $A3 $8C $6E $FF $76 $04 $E8 $C9 $B8 $59 $A1 $0C $71 $8B $16 $0A $71 $81 $C2 $00 $60 $83 $D0 $FF $89 $16 $FC $70 $A3 $FE $70 $A1 $10 $71 $8B $16 $0E $71 $81 $C2 $00 $B0 $83 $D0 $FF $89 $16 $00 $71 $A3 $02 $71 $A1 $FE $70 $8B $16 $FC $70 $3B $06 $78 $58 $7F $16 $7C $06 $3B $16 $76 $58 $73 $0E $A1 $78 $58 $8B $16 $76 $58 $89 $16 $FC $70 $A3 $FE $70 $A1 $02 $71 $8B $16 $00 $71 $3B $06 $7C $58 $7F $16 $7C $06 $3B $16 $7A $58 $73 $0E $A1 %patch 0x822C $7C $58 $8B $16 $7A $58 $89 $16 $00 $71 $A3 $02 $71 $A1 $FE $70 $8B $16 $FC $70 $3B $06 $EC $81 $7C $16 $7F $06 $3B $16 $EA $81 $76 $0E $A1 $EC $81 $8B $16 $EA $81 $89 $16 $FC $70 $A3 $FE $70 $A1 $02 $71 $8B $16 $00 $71 $3B $06 $F0 $81 $7C $16 $7F $06 $3B $16 $EE $81 $76 $0E $A1 $F0 $81 $8B $16 $EE $81 $89 $16 $00 $71 $A3 $02 $71 $E8 $B7 $7F $E8 $AD $AB $C7 $06 $0E $99 $01 $00 $E8 $1E $3F $E8 $FC $7F $E8 $F9 $7F $E8 $5A $AB $8B $16 $FE $70 $A1 $FC $70 $B1 $0C $E8 $3F $69 $A3 $82 $58 $8B $16 $02 $71 $A1 $00 $71 $B1 $0C $E8 $30 $69 $A3 $3E $5D $31 $C0 $A3 $6A $58 $A3 $6C $58 $A3 $61 $53 $A3 $63 $53 $B0 $0F $A3 $40 $5D $EB $03
#Modify demo code in handle_ctrl, so it IS checked if end of file has arrived
#(with an end-of-demo 16-bit pointer being stored in keen_gp.unknown for now...)
%patch 0x1FA1 $55 $89 $E5 $83 $EC $0E $83 $3E $AC $96 $00 $74 $0A $83 $3E $AC $96 $02 $74 $03 $E9 $89 $00 $8B $5E $08 $D1 $E3 $8B $9F $BE $82 $83 $FB $03 $77 $4A $D1 $E3 $2E $FF $A7 $8B $20 $8D $46 $FA $16 $50 $16 $8D $46 $F2 $50 $E8 $24 $FB $83 $C4 $04 $EB $26 $8D $46 $FA $16 $50 $16 $8D $46 $F2 $50 $E8 $4D $FC $83 $C4 $04 $EB $14 $8D $46 $FA $16 $50 $D1 $EB $4B $53 $16 $8D $46 $F2 $50 $E8 $0F $FE $83 $C4 $06 $16 $8D $46 $F2 $50 $B9 $06 $00 $E8 $DA $CC $83 $3E $AC $96 $02 $75 $5C $8B $46 $FA $C1 $E0 $02 $8B $56 $FE $D1 %patch 0x2021 $E2 $09 $D0 $0B $46 $FC $8B $1E $DE $82 $81 $FB $75 $96 $75 $08 $68 $50 $33 $E8 $88 $F1 $44 $44 $88 $07 $FF $06 $DE $82 $EB $32 $8B $1E $DE $82 $3B $1E $65 $33 $75 $08 $C7 $06 $E8 $70 $02 $00 $EB $20 $8A $07 $98 $89 $C2 $FF $06 $DE $82 $24 $01 $89 $46 $FC $88 $D0 $24 $02 $D0 $F8 $89 $46 $FE $80 $E2 $3C $C0 $FA $02 $89 $56 $FA $FF $76 $06 $FF $76 $04 $8D $46 $FA $16 $50 $B9 $06 $00 $E8 $66 $CC $8B $46 $04 $89 $EC $5D $C3 $CD $1F $DF $1F $F1 $1F $F1 $1F
#Replace continue_game with demo recording handling, along with an internal
#function that reset some things *after* demo recording (like ammo count).
%patch 0xA440 $55 $89 $E5 $83 $EC $05 $16 $6A $01 $6A $1A $E8 $EA $81 $83 $C4 $04 $68 $22 $33 $E8 $EC $83 $44 $44 $E8 $5E $7D $6A $02 $8D $46 $FD $50 $E8 $BD $86 $83 $C4 $04 $09 $C0 $0F $84 $89 $00 $8D $46 $FD $50 $E8 $E3 $34 $44 $44 $A3 $8C $82 $E8 $48 $7C $C7 $06 $83 $53 $00 $00 $6A $00 $E8 $01 $26 $44 $44 $6A $06 $6A $00 $68 $C0 $96 $E8 $1B $3A $83 $C4 $06 $E8 $63 $00 $C7 $06 $E8 $70 $00 $00 $FF $36 $8C $82 $E8 $CB $DC $44 $44 $E8 $45 $89 $E8 $0B $7D $6A $01 $6A $17 $E8 $82 $81 $83 $C4 $04 $68 $3A $33 $E8 $84 $83 $44 %patch 0xA4C0 $44 $E8 $16 $83 $25 $FF $00 $3C $30 $7C $0D $3C $39 $7F $09 $83 $C0 $D0 $50 $E8 $77 $7C $44 $44 $C7 $06 $AC $96 $00 $00 $E8 $51 $89 $C7 $06 $FE $70 $00 $00 $C7 $06 $FC $70 $00 $10 $6A $5A $E8 $BE $61 $44 $44 $E8 $27 $00 $31 $C0 $89 $EC $5D $C3 $55 $89 $E5 $31 $C0 $A3 $4A $9D $A3 $4C $9D $A3 $EE $70 $A3 $F0 $70 $A3 $0A $71 $A3 $0C $71 $A3 $0E $71 $A3 $10 $71 $89 $EC $5D $C3 $55 $89 $E5 $C7 $06 $0A $9D $00 $00 $6A $5A $E8 $84 $61 $44 $44 $C7 $06 $46 $9D $04 $00 $C7 $06 $48 $9D $05 $00 $6A $32 $6A $00 $68 $14 $9D $E8 $6B $39 $83 $C4 $06 $C7 $06 $1A $9D $01 $00 $6A $10 $6A $00 $68 $5E $9D $E8 $58 $39 $83 $C4 $06 $89 $EC $5D $C3
### Let's begin modifying do_intro_and_menu. We want ###
### to shorten the time needed to wait for the demos. ###
%patch 0x99B3 $0092W
#Demo playback patch, modifying most of do_intro_and_menu
%patch 0x9A08 $74 $03 $E8 $A0 $06 $8B $46 $F8 $2B $06 $40 $5D $89 $46 $F8 $09 $C0 $0F $8F $F6 $00 $E9 $A3 $00 $68 $DD $27 $68 $24 $82 $E8 $A0 $47 $83 $C4 $04 $6A $0A $8D $46 $EA $50 $57 $E8 $CA $43 $83 $C4 $06 $8D $46 $EA $50 $68 $24 $82 $E8 $4D $47 $83 $C4 $04 $68 $E2 $27 $68 $24 $82 $E8 $41 $47 $83 $C4 $04 $FF $36 $62 $25 $68 $24 $82 $E8 $34 $47 $83 $C4 $04 $68 $00 $80 $68 $24 $82 $E8 $AC $44 $83 $C4 $04 $83 $F8 $FF $74 $52 $89 $C6 $50 $E8 $5C $41 $44 $44 $05 $EC $82 $A3 $65 $33 $56 $E8 $9F $35 $44 $44 $57 $E8 $54 $86 %patch 0x9A88 $44 $44 $31 $F6 $89 $36 $E8 $70 $89 $36 $83 $53 $56 $E8 $F1 $2F $44 $44 $6A $06 $56 $68 $C0 $96 $E8 $0C $44 $83 $C4 $06 $E8 $54 $0A $FF $36 $8C $82 $E8 $C2 $E6 $44 $44 $C7 $06 $AC $96 $00 $00 $E8 $63 $0A $83 $3E $E8 $70 $01 $74 $08 $47 $83 $FF $0A $0F $8C $56 $FF $31 $FF $E8 $63 $93 $6A $5A $E8 $DC $6B $44 $44 $C7 $46 $F8 $92 $00 $E8 $5B $67 $E8 $AC $67 $C7 $06 $FC $70 $00 $20 $C7 $06 $FE $70 $00 $00 $C7 $06 $00 $71 $00 $20 $C7 $06 $02 $71 $00 $00 $E8 $B0 $26 $E8 $AE $05 $E8 $EF $92 $83 $3E $E8 $70 $01 $0F $84 $2A $00 $E9 $D0 $FE
#Replace text "Continue Game" with "Record A Demo"
%patch 0x1C7E2 " Record A Demo" $0A $00
#A couple of more text replacements for the continue_game function
#(now used for demo recording)
%patch 0x1CB42 "Choose a level (1-99): " $00 %patch 0x1CB5A "Save as demo
#(0-9): " $00
#Also for this
%patch 0x1CB70 "Demo buffer overflow" $00


Keen 1 patch with full commentary

This is the Keen 1 patch with the complete original commentary. It is included here for instruction.

Patch: Keen 1 v1.31, Apogee Software release
### Modify sync_drawing code for proper handling of demo recording/playback
### The basic idea: Swap the roles of ticks and ticks_sync.
### 5135h == ticks, 563Eh == ticks_sync
%patch 0x25F $A1 $563EW
#Store ticks_sync in ax
$2B $06 $5135W
#Subtract ticks from ax and then keep in ax the result
# Do NOT yet store any value in sprite_sync; No need for that anyway.
# We should simply compare ax to 6. For the reasons of
# saving code save, though... we rather compare the register al.
$3C $06
#Compare al to 6 (doing an UNSIGNED comparison)
$72 $F5
#Jump if smaller
# Do NOT store more recent difference (possibly clamped)
# in sprite_sync. Simply set it to 6.
# But first, store 6 in dx:ax.
$B8 $0006W $99
# Now store the value of ax in sprite_sync
$A3 $5B14W
# Rather than storing (old) ticks in (old) ticks_sync,
# we increase ticks (the "new" ticks_sync) by 6, AND store
# the *exact* same value in ticks_sync (the "new" ticks).
# To be more specific, we increase both variables by dx:ax.
# Beginning with ticks:
$01 $06 $5135W $11 $16 $5137W
# Finally, we copy ticks to ticks_sync. Since this is actually
# a portion of the original code, we don't patch ANYTHING here.
#Let us also replace ticks with ticks_sync within int_8_handler
%patch 0xBFAD $563EW
# Add 1 to ticks_sync (lo)
%patch 0xBFB2 $5640W
# Add carry to ticks_sync+2
#Finally, modify draw_level at the very beginning so:
#1. The discussion with the Grand Intellection (in Keen 3 "boss level")
#is skipped (by modifying the value of a variable found for ALL episodes).
#2. ticks and ticks_sync are set to 0 only *after* fade_out, fade_in and co.
#3. sprite_sync is set to 0xF (upper bound) right before the "level loop"
#begins, and sync_drawing is *not* called on the very beginning of the
#very first run of the loop.
#
# *** Constructed from dmo1lvll.asm using NASM v2.09.10 ***
#
%patch 0x4BA3 $C7 $46 $FE $00 $00 $C7 $06 $4C $60 $01 $00 $31 $C0 $A3 $56 $82 $A3 $AA $5D $A3 $60 $6C $FF $76 $04 $E8 $2C $CA $44 $44 $A1 $E0 $6E $8B $16 $DE $6E $81 $C2 $00 $60 $83 $D0 $FF $89 $16 $D0 $6E $A3 $D2 $6E $A1 $E4 $6E $8B $16 $E2 $6E $81 $C2 $00 $B0 $83 $D0 $FF $89 $16 $D4 $6E $A3 $D6 $6E $A1 $D2 $6E $8B $16 $D0 $6E $3B $06 $4C $56 $7F $16 $7C $06 $3B $16 $4A $56 $73 $0E $A1 $4C $56 $8B $16 $4A $56 $89 $16 $D0 $6E $A3 $D2 $6E $A1 $D6 $6E $8B $16 $D4 $6E $3B $06 $50 $56 $7F $16 $7C $06 $3B $16 $4E $56 $73 $0E %patch 0x4C23 $A1 $50 $56 $8B $16 $4E $56 $89 $16 $D4 $6E $A3 $D6 $6E $A1 $D2 $6E $8B $16 $D0 $6E $3B $06 $C0 $7F $7C $16 $7F $06 $3B $16 $BE $7F $76 $0E $A1 $C0 $7F $8B $16 $BE $7F $89 $16 $D0 $6E $A3 $D2 $6E $A1 $D6 $6E $8B $16 $D4 $6E $3B $06 $C4 $7F $7C $16 $7F $06 $3B $16 $C2 $7F $76 $0E $A1 $C4 $7F $8B $16 $C2 $7F $89 $16 $D4 $6E $A3 $D6 $6E $E8 $BF $B5 $E8 $10 $20 $C7 $06 $1E $82 $01 $00 $E8 $B0 $6A $E8 $00 $B6 $E8 $FD $B5 $E8 $BA $1F $8B $16 $D2 $6E $A1 $D0 $6E $B1 $0C $E8 $D7 $94 $A3 $56 $56 $8B $16 $D6 $6E $A1 $D4 $6E $B1 $0C $E8 $C8 $94 $A3 $12 $5B $31 $C0 $A3 $3E $56 $A3 $40 $56 $A3 $35 $51 $A3 $37 $51 $B0 $0F $A3 $14 $5B $EB $03
#Modify demo code in handle_ctrl, so it IS checked if end of file has arrived
#(with an end-of-demo 16-bit pointer being stored in keen_gp.unknown for now...)
#
# *** Constructed from dmo1ctrl.asm using NASM v2.09.10 ***
#
%patch 0x5A39 $55 $89 $E5 $83 $EC $0E $83 $3E $32 $97 $00 $74 $0A $83 $3E $32 $97 $02 $74 $03 $E9 $89 $00 $8B $5E $08 $D1 $E3 $8B $9F $3C $83 $83 $FB $03 $77 $4A $D1 $E3 $2E $FF $A7 $23 $5B $8D $46 $FA $16 $50 $16 $8D $46 $F2 $50 $E8 $31 $FB $83 $C4 $04 $EB $26 $8D $46 $FA $16 $50 $16 $8D $46 $F2 $50 $E8 $57 $FC $83 $C4 $04 $EB $14 $8D $46 $FA $16 $50 $D1 $EB $4B $53 $16 $8D $46 $F2 $50 $E8 $15 $FE $83 $C4 $06 $16 $8D $46 $F2 $50 $B9 $06 $00 $E8 $F3 $87 $83 $3E $32 $97 $02 $75 $5C $8B $46 $FA $C1 $E0 $02 $8B $56 $FE $D1 %patch 0x5AB9 $E2 $09 $D0 $0B $46 $FC $8B $1E $62 $83 $81 $FB $F9 $96 $75 $08 $68 $5E $32 $E8 $92 $B7 $44 $44 $88 $07 $FF $06 $62 $83 $EB $32 $8B $1E $62 $83 $3B $1E $73 $32 $75 $08 $C7 $06 $BC $6E $02 $00 $EB $20 $8A $07 $98 $89 $C2 $FF $06 $62 $83 $24 $01 $89 $46 $FC $88 $D0 $24 $02 $D0 $F8 $89 $46 $FE $80 $E2 $3C $C0 $FA $02 $89 $56 $FA $FF $76 $06 $FF $76 $04 $8D $46 $FA $16 $50 $B9 $06 $00 $E8 $7F $87 $8B $46 $04 $89 $EC $5D $C3 $65 $5A $77 $5A $89 $5A $89 $5A
#Replace continue_game with demo recording handling, along with an internal
#function that reset some things *after* demo recording (like ammo count).
#
# *** Constructed from dmo1rec.asm using NASM v2.09.10 ***
#
%patch 0x9C4E $55 $89 $E5 $83 $EC $05 $16 $6A $01 $6A $1A $E8 $62 $C4 $83 $C4 $04 $68 $30 $32 $E8 $6D $C6 $44 $44 $E8 $D0 $BF $6A $02 $8D $46 $FD $50 $E8 $44 $C9 $83 $C4 $04 $09 $C0 $0F $84 $89 $00 $8D $46 $FD $50 $E8 $5F $32 $44 $44 $A3 $04 $83 $E8 $B2 $BE $C7 $06 $57 $51 $00 $00 $6A $00 $E8 $7D $23 $44 $44 $6A $06 $6A $00 $68 $D0 $7F $E8 $97 $37 $83 $C4 $06 $E8 $63 $00 $C7 $06 $BC $6E $00 $00 $FF $36 $04 $83 $E8 $B4 $AE $44 $44 $E8 $8F $CF $E8 $7D $BF $6A $01 $6A $17 $E8 $FA $C3 $83 $C4 $04 $68 $48 $32 $E8 $05 $C6 $44 %patch 0x9CCE $44 $E8 $96 $C5 $25 $FF $00 $3C $30 $7C $0D $3C $39 $7F $09 $83 $C0 $D0 $50 $E8 $E5 $BE $44 $44 $C7 $06 $32 $97 $00 $00 $E8 $9E $CF $C7 $06 $D2 $6E $00 $00 $C7 $06 $D0 $6E $00 $10 $6A $5A $E8 $6B $69 $44 $44 $E8 $27 $00 $31 $C0 $89 $EC $5D $C3 $55 $89 $E5 $31 $C0 $A3 $CA $AA $A3 $CC $AA $A3 $C2 $6E $A3 $C4 $6E $A3 $DE $6E $A3 $E0 $6E $A3 $E2 $6E $A3 $E4 $6E $89 $EC $5D $C3 $55 $89 $E5 $C7 $06 $8E $AA $00 $00 $6A $5A $E8 $31 $69 $44 $44 $C7 $06 $C6 $AA $04 $00 $C7 $06 $C8 $AA $00 $00 $6A $32 $6A $00 $68 $94 $AA $E8 $E7 $36 $83 $C4 $06 $C7 $06 $9A $AA $00 $00 $6A $10 $6A $00 $68 $DE $AA $E8 $D4 $36 $83 $C4 $06 $89 $EC $5D $C3
### Let's begin modifying do_intro_and_menu. We want ###
### to shorten the time needed to wait for the demos. ###
%patch 0x90D5 $0092W
#Demo playback patch, modifying most of do_intro_and_menu
#
# *** Constructed from dmo1play.asm using NASM v2.09.10 ***
#
%patch 0x912A $74 $03 $E8 $A4 $06 $2B $3E $14 $5B $09 $FF $0F $8F $F6 $00 $E9 $A3 $00 $68 $81 $27 $68 $90 $82 $E8 $0E $46 $83 $C4 $04 $6A $0A $8D $46 $EA $50 $56 $E8 $38 $42 $83 $C4 $06 $8D $46 $EA $50 $68 $90 $82 $E8 $BB $45 $83 $C4 $04 $68 $86 $27 $68 $90 $82 $E8 $AF $45 $83 $C4 $04 $FF $36 $06 $25 $68 $90 $82 $E8 $A2 $45 $83 $C4 $04 $68 $00 $80 $68 $90 $82 $E8 $1A $43 $83 $C4 $04 $83 $F8 $FF $74 $52 $89 $C7 $50 $E8 $CA $3F $44 $44 $05 $70 $83 $A3 $73 $32 $57 $E8 $0D $34 $44 $44 $56 $E8 $B0 $C9 $44 $44 $31 $FF $89 $3E %patch 0x91AA $BC $6E $89 $3E $57 $51 $57 $E8 $5F $2E $44 $44 $6A $06 $57 $68 $D0 $7F $E8 $7A $42 $83 $C4 $06 $E8 $46 $0B $FF $36 $04 $83 $E8 $9D $B9 $44 $44 $C7 $06 $32 $97 $00 $00 $E8 $55 $0B $83 $3E $BC $6E $01 $74 $08 $46 $83 $FE $0A $0F $8C $56 $FF $31 $F6 $E8 $A2 $DA $6A $5A $E8 $7B $74 $44 $44 $BF $92 $00 $E8 $41 $70 $E8 $8E $70 $C7 $06 $D0 $6E $00 $20 $C7 $06 $D2 $6E $00 $00 $C7 $06 $D4 $6E $00 $20 $C7 $06 $D6 $6E $00 $00 $E8 $20 $25 $E8 $BA $05 $E8 $2D $DA $83 $3E $BC $6E $01 $0F $84 $2D $00 $E9 $D8 $FE
#Replace text "Continue Game" with "Record A Demo"
%patch 0x15EF3 " Record A Demo" $0A $00
#A couple of more text replacements for the continue_game function
#(now used for demo recording)
%patch 0x16280 "Choose a level (1-99): " $00 %patch 0x16298 "Save as demo
#(0-9): " $00
#Also for this
%patch 0x162AE "Demo buffer overflow" $00


Original source asm files for Keen 1

These are the original ASM files used to create the patch for Keen 1. They are included here for instruction.

Patch: dmo1ctrl.asm
%define DEMO_OFF 0
%define DEMO_PLAY 1
%define DEMO_RECORD 2

%define CONTROL_KEYB 0
%define CONTROL_MOUSE 1
%define CONTROL_JOY_1 2
%define CONTROL_JOY_2 3

%define quit_to_title 0x6EBC
%define demo_action_ptr 0x8362
%define demo_status 0x9732
; This actually FOLLOWS a few strings we may output (like overflow error) from
; continue_game (now demo recording); Since there is still some space left.
%define end_of_demo_ptr 0x3273
; Here, we specify a few offsets, due to more direct manipulations.
%define overflow_message_offset 0x325E
%define keen_gp_offset 0xAA94
%define ctrl_type_offset 0x833C
%define demo_after_last_byte_char_offset 0x96F9

; (Functions) Offsets
%define chg_vid_and_error 0x1261
%define get_keyb_ctrl_state 0x55A3
%define get_mouse_ctrl 0x56DB
%define get_joystick_ctrl 0x58AF
%define N_SCOPY@ 0xE29B

%define this_func_offset 0x5A39

%define input_device_control -0Eh
%define demo_action -8
%define input_ret -6
%define input_offs 4
%define input_seg 6
%define input_type 8

;;;;;; Modified handle_ctrl ;;;;;;

		push	bp
		mov	bp, sp
		sub	sp, 0Eh		; int
		cmp	word demo_status, DEMO_OFF
		jz	short NotPlayingDemo
		cmp	word demo_status, DEMO_RECORD
		jz	short NotPlayingDemo
		jmp	PlayingDemo	; demo_status =	1
; ---------------------------------------------------------------------------

NotPlayingDemo:
		mov	bx, bp+input_type
		shl	bx, 1
		mov	bx, ctrl_type_offset+bx
		cmp	bx, CONTROL_JOY_2 ; switch 4 cases
		ja	short SaveAsDemo ; default
		shl	bx, 1
		jmp	word cs:(this_func_offset+user_input_type+bx)	; switch jump

keyb:
		lea	ax, bp+input_ret ; case 0x0
		push	ss
		push	ax
		push	ss
		lea	ax, bp+input_device_control
		push	ax		; src_ptr
		call	-this_func_offset+get_keyb_ctrl_state
		add	sp, 4
		jmp	short copy_new_input ; int
; ---------------------------------------------------------------------------

mouse:
		lea	ax, bp+input_ret ; case 0x1
		push	ss
		push	ax
		push	ss
		lea	ax, bp+input_device_control
		push	ax		; src_ptr
		call	-this_func_offset+get_mouse_ctrl
		add	sp, 4
		jmp	short copy_new_input
; ---------------------------------------------------------------------------

joy:
		lea	ax, bp+input_ret ; case 0x2/0x3
		push	ss
		push	ax
		shr	bx, 1
		dec	bx
		push	bx
		push	ss
		lea	ax, bp+input_device_control
		push	ax		; src_ptr
		call	-this_func_offset+get_joystick_ctrl
		add	sp, 6
; ---------------------------------------------------------------------------

copy_new_input:
		push	ss		; src
		lea	ax, bp+input_device_control
		push	ax
		mov	cx, 6
		call	near -this_func_offset+N_SCOPY@

SaveAsDemo:
		cmp	word demo_status, DEMO_RECORD
		jnz	short return
		mov	ax, bp-6 ; Direction
		shl	ax, 2
		mov	dx, bp-2 ; Pogo?
		shl	dx, 1
		or	ax, dx
		or	ax, bp-4 ; Jump?
		mov	bx, demo_action_ptr
		cmp	bx, demo_after_last_byte_char_offset
		jnz	short DoSaveDemo
OverflowError:

		push	word overflow_message_offset
		call	-this_func_offset+chg_vid_and_error
		inc	sp
		inc	sp
DoSaveDemo:

		mov	bx, al
		inc	word demo_action_ptr
		jmp	short return
; ---------------------------------------------------------------------------

PlayingDemo:
		mov	bx, demo_action_ptr
		cmp	bx, end_of_demo_ptr
		jnz	short get_recorded_input

		mov	word quit_to_title, 2 ; SPECIAL VALUE (HALT BY DEMO, *NOT* PLAYER)
		jmp	short return

get_recorded_input:

		mov	al, bx
		cbw
		mov	dx, ax
		inc	word demo_action_ptr

		and	al, 1
		mov	bp-4, ax ; Jump?
		mov	al, dl
		and	al, 2
		sar	al, 1
		mov	bp-2, ax ; Pogo?

		; Last usage of "ax"... so simply use "dx".

		and	dl, 3Ch
		sar	dl, 2
		mov	bp-6, dx   ; Direction

return:
		push	word bp+input_seg
		push	word bp+input_offs	; dst
		lea	ax, bp+input_ret
		push	ss		; src
		push	ax
		mov	cx, 6
		call	near -this_func_offset+N_SCOPY@
		mov	ax, bp+input_offs
		mov	sp, bp
		pop	bp
		retn
; ---------------------------------------------------------------------------
user_input_type:
		dw (this_func_offset+keyb)		; jump table for switch	statement
		dw (this_func_offset+mouse)
		dw (this_func_offset+joy)
		dw (this_func_offset+joy)

Patch: dmo1lvll.asm
%define player_sprite_offset 0x6EDA
; (Global variables given by offsets in the dseg)
%define ticks_lo 0x5135
%define ticks_hi 0x5137
%define scrollX_min_lo 0x564A
%define scrollX_min_hi 0x564C
%define scrollY_min_lo 0x564E
%define scrollY_min_hi 0x5650
%define ticks_sync_lo 0x563E
%define ticks_sync_hi 0x5640
%define scrollX_T 0x5656
%define scrollY_T 0x5B12
%define sprite_sync 0x5B14
%define god_mode 0x5DAA
%define keen_facing 0x604C
%define level_finished 0x6C60
%define scrollX_lo 0x6ED0
%define scrollX_hi 0x6ED2
%define scrollY_lo 0x6ED4
%define scrollY_hi 0x6ED6
%define player_sprite_posX_lo player_sprite_offset+4
%define player_sprite_posX_hi player_sprite_offset+6
%define player_sprite_posY_lo player_sprite_offset+8
%define player_sprite_posY_hi player_sprite_offset+0Ah
%define scrollX_max_lo 0x7FBE
%define scrollX_max_hi 0x7FC0
%define scrollY_max_lo 0x7FC2
%define scrollY_max_hi 0x7FC4
%define lights 0x821E
%define keen_invincible 0x8256

; (Functions) Offsets
%define sync_drawing 0x239
%define draw_screen 0x289
%define init_level 0x15EB
%define fade_in 0x6C49
%define fade_out 0x6C8D
%define clear_overlay 0xB736
%define N_LXRSH@ 0xE172

; THE FOLLOWING ARE A MUST
%define this_code_offset 0x4BA3
%define after_code_offset 0x4CC2

;Function argument
%define levelnum 4
%define about_to_encounter_mortimer -2

; Do NOT show the discussion with the Grand Intellect in Keen 3 "boss level"
; - this may easily result in malformed demo playback.
mov	word bp+about_to_encounter_mortimer, 0
mov	word keen_facing, 1

;Skip these:

;xor	ax, ax
;mov	sprite_sync, ax
;xor	dx, dx
;mov	word ticks_sync, ax
;mov	word ticks_sync+2, dx
;mov	word ticks,	ax
;mov	word ticks+2, dx

;Move them to the end.

xor	ax, ax
mov	keen_invincible, ax
mov	god_mode, ax
mov	level_finished,	ax
push	word bp+levelnum	; levelnum
call	-this_code_offset+init_level
inc	sp
inc	sp
mov	ax, word player_sprite_posX_hi
mov	dx, word player_sprite_posX_lo
add	dx, 6000h
adc	ax, 0FFFFh
mov	word scrollX_lo, dx
mov	word scrollX_hi, ax
mov	ax, word player_sprite_posY_hi
mov	dx, word player_sprite_posY_lo
add	dx, 0B000h
adc	ax, 0FFFFh
mov	word scrollY_lo, dx
mov	word scrollY_hi, ax
mov	ax, word scrollX_hi
mov	dx, word scrollX_lo
cmp	ax, word scrollX_min_hi
jg	short loc_14C23
jl	short loc_14C15
cmp	dx, word scrollX_min_lo
jnb	short loc_14C23

loc_14C15:
mov	ax, word scrollX_min_hi
mov	dx, word scrollX_min_lo
mov	word scrollX_lo, dx
mov	word scrollX_hi, ax

loc_14C23:
mov	ax, word scrollY_hi
mov	dx, word scrollY_lo
cmp	ax, word scrollY_min_hi
jg	short loc_14C46
jl	short loc_14C38
cmp	dx, word scrollY_min_lo
jnb	short loc_14C46

loc_14C38:
mov	ax, word scrollY_min_hi
mov	dx, word scrollY_min_lo
mov	word scrollY_lo, dx
mov	word scrollY_hi, ax

loc_14C46:
mov	ax, word scrollX_hi
mov	dx, word scrollX_lo
cmp	ax, word scrollX_max_hi
jl	short loc_14C69
jg	short loc_14C5B
cmp	dx, word scrollX_max_lo
jbe	short loc_14C69

loc_14C5B:
mov	ax, word scrollX_max_hi
mov	dx, word scrollX_max_lo
mov	word scrollX_lo, dx
mov	word scrollX_hi, ax

loc_14C69:
mov	ax, word scrollY_hi
mov	dx, word scrollY_lo
cmp	ax, word scrollY_max_hi
jl	short loc_14C8C
jg	short loc_14C7E
cmp	dx, word scrollY_max_lo
jbe	short loc_14C8C

loc_14C7E:
mov	ax, word scrollY_max_hi
mov	dx, word scrollY_max_lo
mov	word scrollY_lo, dx
mov	word scrollY_hi, ax

loc_14C8C:
call	-this_code_offset+sync_drawing
call	-this_code_offset+fade_out
mov	word lights,	1
call	-this_code_offset+clear_overlay
call	-this_code_offset+draw_screen
call	-this_code_offset+draw_screen
call	-this_code_offset+fade_in
mov	dx, word scrollX_hi
mov	ax, word scrollX_lo
mov	cl, 0Ch
call	near -this_code_offset+N_LXRSH@
mov	scrollX_T, ax
mov	dx, word scrollY_hi
mov	ax, word scrollY_lo
mov	cl, 0Ch
call	near -this_code_offset+N_LXRSH@
mov	scrollY_T, ax

; Finally, the relocated code
;xor	ax, ax
;mov	sprite_sync, ax
;xor	dx, dx
;mov	word ticks_sync_lo, ax
;mov	word ticks_sync_hi, dx
;mov	word ticks_lo,	ax
;mov	word ticks_hi, dx

; Unfortunately it's a bit more complicated. Basically, in order to ensure
; proper sync, sprite_sync should be set to 6 during all playthrough. However,
; if that's done then a minor glitch may be observed on level load, with the
; player falling to a floor it should stand on for a quite short while.
; Solution: Simulate non-patched behaviors of having a relatively *large* value
; in sprite_sync (close to 100 or 64h). Or even simpler: Because sprite_sync is
; clamped to 0Fh in sync_drawing, we can simply set it to 0Fh right here.

xor	ax, ax
mov	word ticks_sync_lo, ax
mov	word ticks_sync_hi, ax
mov	word ticks_lo,	ax
mov	word ticks_hi, ax
mov	al, 0Fh ; SAVING ONE LITTLE (BUT IMPORTANT) BYTE...
mov	word sprite_sync, ax
jmp	short -this_code_offset+after_code_offset+3

Patch: dmo1play.asm
;;; WARNING WARNING WARNING ;;;
;;; The following can easily change based on a different patch ;;;
%define reset_player_partial_state_before_demo_func 0xBD+continue_game
%define reset_player_partial_state_after_demo_func 0xDE+continue_game

; HACK HACK HACK
; There are 6 bytes used some for input (a second variable of that kind!)
; but these become unused with THIS patch. Using it for some numeric string...
%define var_16 -16h

%define DEMO_OFF 0

%define menu_offset 0x9100
%define this_code_offset 0x912A
%define check_menu_repeat_offset 0x922F
%define display_menu_itself_offset 0x9252

; A few string offsets we re-use
%define aDemo 0x2781
%define a__0 0x2786
%define string_buf 0x8290

; (Global variables given by offsets in the dseg)
%define pExt 0x2506
%define rnd 0x5157
%define sprite_sync 0x5B14
%define quit_to_title 0x6EBC
%define extra_life_pts_lo 0x6EC2
%define extra_life_pts_hi 0x6EC4
%define scrollX_lo 0x6ED0
%define scrollX_hi 0x6ED2
%define scrollY_lo 0x6ED4
%define scrollY_hi 0x6ED6
%define current_level 0x8304
%define demo_status 0x9732
%define resuming_saved_game 0xAA8E
; This actually FOLLOWS a few strings we may output (like overflow error) from
; continue_game (now demo recording); Since there is still some space left.
%define end_of_demo_ptr 0x3273
; Here, we specify just the offset, due to more direct manipulations.
%define input_old_offset 0x7FD0
%define keen_gp_offset 0xAA94
; Here too (this is really the beginning of the demo file contents)
%define demo_level_offset 0x8370

; (Functions) Offsets
%define sync_drawing 0x239
%define draw_screen 0x289
%define load_level_data 0x66B
%define draw_level 0x4B69
%define load_demo 0x5B54
%define fade_in 0x6C49
%define fade_out 0x6C8D
%define do_draw_mural 0x97D3
%define continue_game 0x9C4E
%define clear_overlay 0xB736
%define setup_jump_heights 0xC013
%define init_rnd 0xC0AC
%define access 0xC552
%define close 0xC5AB
%define filelength 0xD15c
%define itoa 0xD38A
%define memset 0xD439
%define open 0xD49E
%define strcat 0xD71A
%define strcpy 0xD753

; Alright, let's begin from here!

jz	after_mural_refresh
call	-this_code_offset+do_draw_mural

after_mural_refresh:

; CURRENTLY di stores ticks. It is NOT the case while demo playback is in effect.
;mov	ax, di
;sub	ax, sprite_sync
;mov	di, ax
;or	ax, ax
sub	di, sprite_sync
or	di, di
jg	-this_code_offset+check_menu_repeat_offset
jmp	demo_loop_check

prepare_demo_playback:

; Check if demo file exists (yeah, better to do it in load_demo...but nvm)

push	word aDemo
push	word string_buf
call	-this_code_offset+strcpy
add	sp, 4
push	word 0xA
lea	ax, bp+var_16
push	ax
push	si
call	-this_code_offset+itoa
add	sp, 6
lea	ax, bp+var_16
push	ax
push	word string_buf
call	-this_code_offset+strcat
add	sp, 4
push	word a__0
push	word string_buf
call	-this_code_offset+strcat
add	sp, 4
push	word pExt
push	word string_buf
call	-this_code_offset+strcat
add	sp, 4
push	word 8000h
push	word string_buf
call	-this_code_offset+open
add	sp, 4
cmp	ax, 0FFFFh
jz	demo_loop_increase
mov	di, ax
push	ax
call	-this_code_offset+filelength
inc	sp
inc	sp
add	ax, demo_level_offset
; HACK HACK HACK
mov	word end_of_demo_ptr, ax
; Don't forget to close the file...
push	di
call	-this_code_offset+close
inc	sp
inc	sp

; Finally, load demo!
push	si
call	near -this_code_offset+load_demo
inc	sp
inc	sp
; A few minor preparation should still be done for the sake consistency
; For instance, this:
xor	di, di
mov	word quit_to_title, di
; Handle random number generator
mov	word rnd, di
;push	di
;call	near -this_code_offset+init_rnd
;inc	sp
;inc	sp
; Similarly do not take advantage of time here, either.
push	di
call	near -this_code_offset+setup_jump_heights
inc	sp
inc	sp
; May be important as well...
push	word 6
push	di
push	word input_old_offset
call	-this_code_offset+memset
add	sp, 6
; Also do this BEFORE demo recording; But NOT
; what's done in reset_player_partial_state_after_demo_func.
call	near -this_code_offset+reset_player_partial_state_before_demo_func
; Play demo!
push	word current_level
call	near -this_code_offset+draw_level
inc	sp
inc	sp
; Don't forget these!
mov	word demo_status, DEMO_OFF
call	near -this_code_offset+reset_player_partial_state_after_demo_func

cmp	word quit_to_title, 1 ; 1 - player halt. 2 - Demo halt (may also be 0).
jz	refresh_mural

demo_loop_increase:

inc	si

demo_loop_check:

cmp	si, 0Ah
jl	prepare_demo_playback

refresh_mural:

xor	si, si
; And update later
call	near -this_code_offset+fade_out

; Load menu level
push	word 5Ah
call	near -this_code_offset+load_level_data
inc	sp
inc	sp

;960h (2400) is too long... Let's go for 1 second, or (approximately) 92h.
mov	di, 92h
;mov	di, 960h
call	near -this_code_offset+sync_drawing
call	near -this_code_offset+draw_screen
mov	word scrollX_lo, 2000h
mov	word scrollX_hi, 0
mov	word scrollY_lo, 2000h
mov	word scrollY_hi, 0
call	near -this_code_offset+clear_overlay
call	near -this_code_offset+do_draw_mural
call	near -this_code_offset+fade_in

cmp	word quit_to_title, 1 ; PLAYER halt? (NOT demo, i.e. 2.)
jz	-this_code_offset+display_menu_itself_offset
jmp	-this_code_offset+menu_offset

Patch: dmo1rec.asm
struc GameProfile ;	(sizeof=0x5C)
.stuff:		resw 9
.levels:		resw 16
.lives:		resw 1
.ammo:		resw 1
.score:		resd 1
.mapX:		resd 1			; keen wmap location
.mapY:		resd 1
.screenX:		resd 1			; wmap screen position
.screenY:		resd 1
.targets:		resw 8		; 1 = saved, 0 = notsaved
.unknown:		resw 1
endstruc

%define DEMO_OFF 0

; Episode specific constants
%define const_initial_ammo 0
%define const_have_pogo 0

%define player_sprite_offset 0x6EDA

; (Global variables given by offsets in the dseg)
%define rnd 0x5157
%define quit_to_title 0x6EBC
%define extra_life_pts_lo 0x6EC2
%define extra_life_pts_hi 0x6EC4
%define scrollX_lo 0x6ED0
%define scrollX_hi 0x6ED2
%define player_sprite_posX_lo player_sprite_offset+4
%define player_sprite_posX_hi player_sprite_offset+6
%define player_sprite_posY_lo player_sprite_offset+8
%define player_sprite_posY_hi player_sprite_offset+0Ah
%define current_level 0x8304
%define demo_status 0x9732
%define resuming_saved_game 0xAA8E
%define on_world_map 0xAAFC

; Here, we specify just the offsets, due to more direct manipulations.
%define input_old_offset 0x7FD0
%define keen_gp_offset 0xAA94

; (Functions) Offsets
%define load_level_data 0x66B
%define chg_vid_and_error 0x1261
%define draw_level 0x4B69
%define record_demo 0x5B3D
%define save_demo 0x5BC9
%define clear_keys 0x5C3A
%define draw_box_opening2 0x60BE
%define read_char_with_echo 0x6268
%define draw_string 0x62D2
%define get_string_input 0x65B7
%define fade_in 0x6C49
%define fade_out 0x6C8D
%define wait_for_key 0xB185
%define setup_jump_heights 0xC013
%define init_rnd 0xC0AC
%define atoi 0xCEE2
%define memset 0xD439

; String OFFSETS in the dseg (They MUST be there; draw_string depends on this.)
;YouCanRecordALevel db "You can RECORD a level\n"
;%define YouCanRecordALevel 0x3130
;OnlyFromTheWorldMap db "ONLY from the World Map!\n"
;%define OnlyFromTheWorldMap 0x3148
;PressAKey db "    press a key:"
;%define PressAKey 0x3162
;ChooseALevel db "Choose a level (1-99): "
%define ChooseALevel 0x3230
;SaveAsDemo db "Save as demo 
#(0-9): "
%define SaveAsDemo 0x3248 ;DemoHasBeenSaved db "Demo has been saved." ;%define DemoHasBeenSaved 0x31A1 ;DemoHasNotBeenSaved db "Demo has NOT been saved." ;%define DemoHasNotBeenSaved 0x31B6 ; Finally (THIS IS IMPORTANT): The offset (in segment 0) where the function begins %define this_func_offset 0x9C4E ;BASED ON: ;continue_game proc near ;demostr = dword ptr -3 %define demostr -3 push bp mov bp, sp sub sp, 5 push ss ; cmp word on_world_map, 0 ; jnz short do_record_demo ; ;mov ax, 3 ; ;push ax ; ;mov ax, 18h ; ;push ax ; push word 3 ; push word 18h ; call near -this_func_offset+draw_box_opening2 ; add sp, 4 ; ;mov ax, YouCanRecordALevel ; ;push ax ; push word YouCanRecordALevel ; call near -this_func_offset+draw_string ; draw string on light background ; inc sp ; inc sp ; ;mov ax, OnlyFromTheWorldMap ; ;push ax ; push word OnlyFromTheWorldMap ; call near -this_func_offset+draw_string ; draw string on light background ; inc sp ; inc sp ; ;mov ax, PressAKey ; ;push ax ; push word PressAKey ; call near -this_func_offset+draw_string ; draw string on light background ; inc sp ; inc sp ; call near -this_func_offset+clear_keys ; call near -this_func_offset+wait_for_key ; jmp return ; --------------------------------------------------------------------------- do_record_demo: ;mov ax, 1 ;push ax ;mov ax, 1Bh ;push ax push word 1 push word 1Ah call near -this_func_offset+draw_box_opening2 add sp, 4 ;mov ax, ChooseALevel ;push ax push word ChooseALevel call near -this_func_offset+draw_string ; draw string on light background inc sp inc sp ;mov ax, 2 ;push ax call -this_func_offset+clear_keys push word 2 lea ax, bp+demostr push ax call near -this_func_offset+get_string_input add sp, 4 or ax, ax jz return lea ax, bp+demostr push ax call near -this_func_offset+atoi ; NOTE: Never used in Keen1 so far. (atol is, though.) inc sp inc sp ; Time to prepare for demo recording... mov current_level, ax call near -this_func_offset+record_demo ; Do NOT use time as a seed ;push word 0 ;call near -this_func_offset+init_rnd ;inc sp ;inc sp mov word rnd, 0 ; Similarly do not take advantage of time here, either. ;mov ax, 0 ;push ax push word 0 call near -this_func_offset+setup_jump_heights inc sp inc sp ; May be important as well... push word 6 push word 0 push word input_old_offset call -this_func_offset+memset add sp, 6 ; Also do this BEFORE demo recording; But NOT ; what's done in reset_player_partial_state_after_demo_func. call near reset_player_partial_state_before_demo_func ; Just in case it's been set to a nonzero! mov word quit_to_title, 0 ; Load level! push word current_level call near -this_func_offset+draw_level inc sp inc sp ; Maybe fade_out has just been called, so... call near -this_func_offset+fade_in ; Also this: call near -this_func_offset+clear_keys ; Alright, ask player for demo number to select for saving ;mov ax, 1 ;push ax ;mov ax, 17h ;push ax push word 1 push word 17h call near -this_func_offset+draw_box_opening2 add sp, 4 ;mov ax, SaveAsDemo ;push ax push word SaveAsDemo call near -this_func_offset+draw_string ; draw string on light background inc sp inc sp call near -this_func_offset+read_char_with_echo and ax, 0FFh cmp al, 30h ; '0' jl short restore_menu cmp al, 39h ; '9' jg short restore_menu ; Save! add ax, -30h push ax call near -this_func_offset+save_demo inc sp inc sp ; It's much more complicated to restore the world map from ; this point... so, quit with a confirmation. ; push word DemoHasBeenSaved ; jmp short quit_with_demo_message ; push word DemoHasNotBeenSaved ;quit_with_demo_message: ; call near -this_func_offset+chg_vid_and_error ; inc sp ; inc sp restore_menu: ; Don't forget this! mov word demo_status, DEMO_OFF ;;; Prepare menu ;;; ; But first, fade out (possibly for a second time...) call -this_func_offset+fade_out ; HACK to force fade_in to the menu mov word scrollX_hi, 0 mov word scrollX_lo, 0x1000 ;;; Load menu level ;;; push word 5Ah call -this_func_offset+load_level_data inc sp inc sp ; ALSO DON'T FORGET TO RESET PLAYER STATE! (Based on code from do_intro_and_menu) call near reset_player_partial_state_after_demo_func return: xor ax, ax ; Do NOT start a new game, and go back to menu. mov sp, bp pop bp retn ; TIME FOR A NEW PROCEDDURE ; It handles partial player state reset, as usually done in draw_worldmap. reset_player_partial_state_before_demo_func: push bp mov bp, sp xor ax, ax mov word keen_gp_offset+GameProfile.score, ax mov word keen_gp_offset+GameProfile.score+2, ax mov word extra_life_pts_lo, ax mov word extra_life_pts_hi, ax ; When certain sprites added to the level, like creatures, ; it is checked if the player is located somewhere. Problem is ; that the player may have not yet been founded... mov word player_sprite_posX_lo, ax mov word player_sprite_posX_hi, ax mov word player_sprite_posY_lo, ax mov word player_sprite_posY_hi, ax mov sp, bp pop bp retn ; ANOTHEr NEW PROCEDDURE ; It handles player state reset (as usually done in do_intro_and_menu) reset_player_partial_state_after_demo_func: push bp mov bp, sp mov word resuming_saved_game, 0 ; Load menu level push word 5Ah call -this_func_offset+load_level_data inc sp inc sp mov word keen_gp_offset+GameProfile.lives, 4 mov word keen_gp_offset+GameProfile.ammo, const_initial_ammo ;Let us use less lines of code thanks to memset. ;Furthermore, reset *both* stuff and levels *at once*. push word 32h push word 0 push word keen_gp_offset+GameProfile.stuff ; levels included call near -this_func_offset+memset add sp, 6 ;push word 12h ;push word 0 ;push word keen_gp_offset+GameProfile.stuff ;call near -this_func_offset+memset ;add sp, 6 mov word keen_gp_offset+GameProfile.stuff+6, const_have_pogo push word 10h push word 0 push word keen_gp_offset+GameProfile.targets call near -this_func_offset+memset add sp, 6 ;push word 20h ;push word 0 ;push word keen_gp_offset+GameProfile.levels ;call near -this_func_offset+memset ;add sp, 6 mov sp, bp pop bp retn


Demo file name

These are the file names used for the demo file. Only one demo file will be created. As noted above, the demo code is disabled and ineffective without applying the demo recording/playback patch.

Patch: Keen
#Keen 1 :
%patch $5B5B $2781W
#Text called from
%patch $157D1 "DEMO" $00 %patch $5BD0 $2788W
#Text called from
%patch $157D8 "DEMO" $00
#Keen 2 :
%patch $212E $26E1W
#Text called from
%patch $19E61 "DEMO" $00 %patch $21A3 $26E8W
#Text called from
%patch $19E68 "DEMO" $00
#Keen 3 :
%patch $20E3 $27DDW
#Text called from
%patch $1BFFD "DEMO" $00 %patch $2154 $27E4W
#Text called from
%patch $1C004 "DEMO" $00


Run demo code with F2 key

This patch allows a player to run the save or load demo code by pressing the F2 key, normally used for turning sound on or off. The code corrupts the memory used for the game. It is possible to make other keys the demo key.

Patch: Run demo code with F2 key
#Save demo with F2 key (Keen 1)
%patch $0CB5 $E8 $4F11W $EB $72
#Load demo with F2 key (Keen 1)
%patch $0CB5 $E8 $4E9CW $EB $72