Patch:Demo
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.
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.
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.)
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
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
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.)
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.
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
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.
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.
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.)
Start of demo chunks
#Keen 4
%patch $5913 $128AW
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.
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.
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.
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.
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.
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.
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
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
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.
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.
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)
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
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
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.
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.
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