; SOKOBAN FOR MENUET v0.1
; Written in pure assembler by Ivushkin Andrey aka Willow
;
; Last changed: July 2, 2004
;
; Main idea, art & graphics
;   Sokofun for Windows 95 by Games 4 Brains
;   and Sokoban 2.3 by Björn Källmark
;
; Level designers:
;
;   Alberto Garcia, Aymeric du Peloux, Brian Kent, David Holland,
;   David W Skinner, Erim Sever, Evgeniy Grigoriev, François Marques,
;   Frantisek Pokorny, Howard Abed,J franklin Mentzer, Jaques Duthen,
;   John C Davis, John Polhemus, Kobus Theron, Lee Haywood, Mario Bonenfant,
;   Martin P Holland, Mic (Jan Reineke), Phil Shapiro, Richard Weston,
;   Sven Egevad, Ken'ichiro Takahashi (takaken), Thinking Rabbit,
;   Yoshio Murase, ZICO (Zbigniew Kornas)
;
; Special thanks to Hirohiko Nakamiya
;
; More credits:
;   Masato Hiramatsu, Kazuo Fukushima, Klaus Clemens
;
; Game uses its own format of levelset files *.LEV
;   with simple run-length compression

; COMPILE WITH FASM

format binary as ""

include 'macros.inc'    ; decrease code size (optional)
include 'CELLTYPE.INC'  ; object identifiers
;include 'debug.inc'

CUR_DIR equ '/sys/games/'       ; change it to appropriate path

SKIN_SIZE       = 11520         ; size of skin file (16x240)

; field dimensions
FLD_LEFT        = 43
FLD_LEFT2       = FLD_LEFT shl 16
FLD_TOP         = 40
FLD_TOP2        = FLD_TOP shl 16
IMG_SIZE        = 16 shl 16+16
SHIFT           = (16 shl 16)
WND_COLOR       = 0x00aabbcc

; level list dimensions
LEVLIST_XY      = FLD_TOP shl 16+45
LEVLIST_SPACING = 10
LEVWND_X        = 320
LEVWND_Y        = 200

; input line dimensions
INP_X           = 10 shl 16+300
INP_Y           = 160 shl 16+16
INP_XY          = 15 shl 16+164

; load button dimensions
LOAD_X          = 130 shl 16+65
LOAD_Y          = 180 shl 16+14
LOAD_XY         = 135 shl 16+184
CHOOSE_XY       = 40 shl 16+148


WIN_XY          = 135 shl 16+25

; workmode constants, more defs in CELLTYPE.INC
WM_WINNER       = 0x10
WM_READSET      = 0
WM_LOSE         = 0x20

use32
  org    0x0
  db     'MENUET01'
  dd     0x01
  dd     START
  dd     I_END
  dd     0x100000
  dd     0x7fff0
  dd     0x0
  dd     0x0

START:
    mov  eax,70         ; load skin image-it is in RAW 16x240 BGR
    mov  ebx,file_info  ; IrfanView recommended
    int  0x40
    test ebx,ebx
;    jmp load_level
;    jz   close
  load_fail:            ; clear input line, also if levelset load failed
    mov  [inp_pos],0
  load_std:
    mov  esi,stdlev
    mov  edi,path_end
    mov  ecx,stdlev_len-stdlev
    rep  movsb
    mov  ecx,10
  reset_fi:
    mov  dword[cnf_level],level_start
    xor  eax,eax
    mov  [levpage],eax
    mov  word[ll_num],'00'   ; reset some counters
  read_cnf:
    mov  eax,70
    mov  ebx,file_info
    int  0x40
    test ebx,ebx        ; load standard levels SOKO-?.LEV instead of custom
    jz   nxt_cnf
    add  dword[cnf_level],ebx
  nxt_cnf:
    test ecx,ecx        ; this check is for loading a custom levelset
    jz   no_increase
    inc  byte[file_num] ; next standard levelset
    loop read_cnf
  no_increase:
    cmp  dword[cnf_level],level_start
    jne  go_on
    test ecx,ecx
    jz   load_fail
    jmp  close          ; missing standard levels & exiting
  go_on:
    mov  eax,[cnf_level]
    mov  byte[eax],0xf0 ; end-of-levels mark

    call read_levelset
  backto_set:
    mov  byte[workmode],WM_READSET
    mov  byte[winmode],0
    jmp  red
restart_level:
    call decode_field   ; uncompress level

red:
    call draw_window

still:

    mov  eax,10
    int  0x40
    cmp  byte[winmode],WM_WINNER
    je   backto_set
    cmp  byte[winmode],WM_LOSE
    je   backto_set
    cmp  eax,1
    je   red
    cmp  eax,2
    je   key
    cmp  eax,3
    je   button

    jmp  still

  key:
    mov  eax,2
    int  0x40

    cmp  byte[workmode],WM_READSET
    jne  key_move
    cmp  ah,32          ; Space moves focus to input line
    je   is_input
    cmp  ah,184
    jne  no_prev
    cmp  [levpage],0    ; PgUp
    jz   still
    sub  [levpage],10
    cmp  byte[ll_num+1],'0'
    jnz  _pu
    dec  byte[ll_num]
    mov  byte[ll_num+1],'9'+1
  _pu:
    dec  byte[ll_num+1]
    jmp  red
  no_prev:
    cmp  ah,183         ; PgDn
    jne  no_next
    mov  eax,[levpage]
    add  eax,10
    cmp  eax,[levelcount]
    jae  still
    mov  [levpage],eax
    cmp  byte[ll_num+1],'9'
    jnz  _pd
    inc  byte[ll_num]
    mov  byte[ll_num+1],'0'-1
  _pd:
    inc  byte[ll_num+1]
    jmp  red
  no_next:
    sub  ah,48
    cmp  ah,9
    ja   still
    movzx eax,ah        ; user selects a level
    add  eax,[levpage]
    cmp  eax,[levelcount]
    jae  still
    mov  eax,[levelmap+eax*4]
    mov  [levptr],eax   ; current level pointer
    mov  al,byte[eax]
    mov  byte[workmode],al
    jmp  restart_level

    ; we're already in game
  key_move:
    cmp  ah,180        ; Home
    je   backto_set
    cmp  ah,176
    jb   no_arrows
    sub  ah,176
    cmp  ah,3
    ja   no_arrows
    movzx ecx,ah
    movzx edx,[player]
    inc  ch
    call valid_move
    cmp  byte[winmode],WM_WINNER
    jne  no_winner
    mov  ecx,0x00ac0000
    mov  edx,win_msg
    mov  esi,win_msg_end-win_msg   ; print victory congratulations
  print_msg:
    mov  ebx,WIN_XY
    mov  eax,4
    int  0x40
    jmp  d_f
  no_winner:
    cmp  byte[winmode],WM_LOSE
    jne  d_f
  no_loser:
    test  al,al         ; no move accepted
    jnz   still
  d_f:
    call draw_field     ; move performed-redraw
    jmp  still
  no_arrows:
    cmp  ah,27
    je   restart_level

    jmp  still

  button:
    mov  eax,17
    int  0x40

    cmp  ah,1
    jne  noclose
  close:
    xor  eax,eax
    dec  eax
    int  0x40           ; shutdown.

  noclose:
    cmp  ah,2
    jne  no_input
  is_input:             ; simple input line with backspace feature
    mov  ebx,[entered]  ; sorry - no cursor
    test ebx,ebx
    jnz  wait_input
    mov  [inp_pos],ebx
    inc  [entered]
  wait_input:
    call draw_input
    mov  eax,10
    int  0x40
    cmp  eax,2
    jne  still
    mov  edi,[inp_pos]
    mov  eax,2
    int  0x40
    shr  eax,8
    cmp  eax,27
    je   still
    cmp  eax,13
    je   load_level
    cmp  eax,8
    je   backsp
    mov  [fn_input+edi],al
    inc  [inp_pos]
    jmp  wait_input
  backsp:
    test edi,edi
    jz   wait_input
    dec  [inp_pos]
    jmp  wait_input
  no_input:
    cmp  ah,3
    jne  no_load
  load_level:
    mov  ecx,[inp_pos]
    test ecx,ecx
    je   load_std
    mov  esi,fn_input
    mov  byte[esi+ecx],0
    inc  ecx
    mov  edi,path_end
    rep  movsb
    jmp  reset_fi
  no_load:
    jmp  still


;   *********************************************
;   ** FILLS LEVEL POINTER MAP ******************
;   *********************************************
read_levelset:

    mov  dword[wnd_width],LEVWND_X
    mov  dword[wnd_height],LEVWND_Y
    mov  [levelcount],0
    mov  edi,level_start
    mov  esi,levelmap
    mov  al,0xff
  rls_cycle:
    cmp  byte[edi],EOF
    je   end_of_levelset
    mov  [esi],edi
    add  esi,4
    mov  ecx,1024
    inc  [levelcount]
    repne scasb
    jecxz eol  ;end_of_levelset
    jmp  rls_cycle
  end_of_levelset:
    mov  eax,[levelcount]
;    debug_print_dec eax
    ret
  eol:
;   debug_print '*** '
    jmp  end_of_levelset

;   *********************************************
;   *******  DEFINE & DRAW WINDOW & OTHER STUFF *
;   *********************************************

draw_window:

    mov  eax,12
    mov  ebx,1
    int  0x40

    mov  eax,0
    mov  ebx,150*65536
    add  ebx,[wnd_width]
    mov  ecx,50*65536
    add  ecx,[wnd_height]
    mov  edx,0x13000000 + WND_COLOR
    mov  esi,0x005080d0
    mov  edi,zagolovok
    int  0x40

    cmp  byte[workmode],WM_READSET
    je   list_levels

    mov  edi,[levptr]   ; print custom level filename
    add  ebx,170*65536
    lea  edx,[edi+4]
    movzx  esi,byte[edi+3]
    int  0x40

    call draw_field
    cmp  [entered],0
    jz   end_of_draw
    mov  edx,fn_input   ; print input line text
    mov  esi,[inp_pos]
    mov  ebx,FLD_LEFT2+FLD_TOP-15
    jmp  draw_level_file

  list_levels:

    call draw_input

    mov  eax,8          ; draw load button
    mov  ebx,LOAD_X
    mov  ecx,LOAD_Y
    mov  edx,3
    mov  esi,WND_COLOR
    int  0x40

    mov  eax,4
    mov  ecx,0x00107a30
    mov  ebx,LOAD_XY
    mov  edx,load_char
    mov  esi,loadlen-load_char
    int  0x40

    mov  ebx,LEVLIST_XY
    mov  edi,0x004e00e7
    xor  esi,esi
    mov  ecx,10
  ll_cycle:
    push ecx esi ebx esi
    lea  ecx,[esi+'0']
    mov  [ll_num+2],cl
    mov  ecx,edi
    mov  edx,ll_num
    mov  esi,4
    int  0x40
    add  ebx,25 shl 16
    pop  esi
    add  esi,[levpage]
    mov  edx,[levelmap+esi*4]
    add  edx,4
    movzx esi,byte[edx-1]
    int  0x40
    pop  ebx esi ecx
    inc  esi
    mov  edx,[levelcount]
    sub  edx,[levpage]
    cmp  edx,esi
    jbe  choose_print
    add  ebx,LEVLIST_SPACING
    loop ll_cycle
  choose_print:
    mov  edx,ll_msg
    mov  esi,ll_msg_end-ll_msg
    mov  ebx,CHOOSE_XY
  draw_level_file:
    mov  eax,4
    int  0x40

  end_of_draw:
    mov  eax,12
    mov  ebx,2
    int  0x40

    ret

;   *********************************************
;   ******* DRAW CELL IMAGES WITHIN FIELD *******
;   *********************************************

draw_field:
    cmp  byte[workmode],sSokonex
    jne  no_chl
    call check_lasers   ; Sokonex game
  no_chl:
    mov  eax,13         ; clear field area
    mov  edx,WND_COLOR
    mov  edi,[levptr]
    movzx ebx,byte[edi+1]
    shl  ebx,4
    lea  ebx, [FLD_LEFT2+ebx]
    movzx ecx,byte[edi+2]
    shl  ecx,4
    lea  ecx, [FLD_TOP shl 16+ecx]
    int  0x40

    mov  edx, FLD_LEFT2+FLD_TOP
    movzx edi,byte[edi+1]
    shl  edi,20
    add  edi, FLD_LEFT2

    xor  eax,eax
    mov  ecx,[fld_size]
    mov  esi,field
  fld_cycle:
    lodsb
    call draw_img
    add  edx,SHIFT
    cmp  edx,edi
    jl  no_nl
    add  edx,16
    and  edx,0xffff
    add  edx,FLD_LEFT2
  no_nl:
    loop fld_cycle
    cmp  byte[workmode],sSokonex
    jne  end_of_df
    call draw_lasers
  end_of_df:
    ret

;   *********************************************
;   *********** DRAW CELL IMAGE *****************
;   *********************************************

draw_img:               ; in: eax-object index, edx-coordinates
    pusha
    cmp  eax,tWall
    jbe  no_adjust
    cmp  [workmode],sSokolor
    jne  no_di_color
    add  eax,pm_col-pm_nex
    jmp  no_adjust
  no_di_color:
    cmp  [workmode],sSokonex
    jne  no_adjust
    inc  eax
  no_adjust:
    movzx  ebx,byte [pic_map+eax]
    cmp  ebx,0xf
    je   no_img
  bl_place:
    mov  ecx, IMG_SIZE
    imul ebx, 256*3
    add  ebx,strip
    mov  eax,7          ; draw_image sysfunc
    int  0x40
  no_img:
    popa
    ret

;****************************************
;******* DRAW CONTENTS OF INPUT LINE ****
;****************************************
draw_input:
    push edi
    cmp  eax,4
    jne  highlight
    mov  esi,WND_COLOR
    jmp  di_draw
  highlight:
    mov  esi,0xe0e0e0
  di_draw:
    mov  eax,8
    mov  ebx,INP_X
    mov  ecx,INP_Y
    mov  edx,2
    int  0x40
    mov  eax,4
    mov  ecx,0x00107a30            ; èà¨äâ 1 ¨ 梥â ( 0xF0RRGGBB )
    mov  ebx,INP_XY
    mov  edx,fn_input
    mov  esi,[inp_pos]
    int  0x40
    pop  edi
    ret

;   ********************************************************
;   * DECOMPRESS LEVEL & FILL SOME TABLES TO CHECK VICTORY *
;   ********************************************************

decode_field:
;    debug_print <13,10>
    xor  eax,eax
    mov  dword[checkpoint],eax
    mov  dword[checkpoint+4],eax
    mov  byte[checkcount],al
    mov  edi,[levptr]
    mov  dl,[edi]
    mov  [workmode],dl
    movzx  edx,byte[edi+1]
    mov  esi,edx
    shl  esi,4
    add  esi,FLD_LEFT*2-25
    mov  [wnd_width],esi
    neg  edx
    mov  [move_map+8],edx
    neg  edx
    mov  [move_map+4],edx
    movzx eax,byte[edi+2]
    mov  esi,eax
    shl  esi,4
    add  esi,FLD_TOP*2-18
    mov  [wnd_height],esi
    imul edx,eax
    mov  [fld_size],edx
    lea  esi,[edi+4]
    movzx ecx,byte[edi+3]
    add  esi,ecx
    cmp  byte[esi],0xff
    je   backto_set
    xor  edi,edi
    cld
 dec_cycle:
    lodsb
    movzx ecx,al
    and  ecx,0xf                   ; ecx-count of objects
    shr  al,4                      ; eax-index of object
    inc  ecx
    sub  edx,ecx
  dc_cycle:
    mov  [field+edi],al
    call chk_win_obj
    jne  no_register
    push eax ecx esi
    movzx ecx,al
    shl  eax,12
    or   eax,edi
    inc  byte[checkcount]
    cmp  [workmode],sSokolor
    jne  chk_sokoban
;    debug_print ':'
;    debug_print_dec ecx
    sub  ecx,tRedB
    shl  ecx,1
    cmp  word[checkpoint+ecx],0
    jnz  no_new_check
    mov  [checkpoint+ecx],ax
    and  eax,0xfff
;    debug_print_dec eax
    jmp  no_new_check
  chk_sokoban:
    cmp  [workmode],sSokonex
    jne  no_nex
    cmp  byte[checkcount],1
    ja   no_new_check
  no_nex:
    movzx ecx,byte[checkcount]
    mov  word[checkpoint-2+ecx*2],ax
  no_new_check:
    pop  esi ecx eax
  no_register:
    inc  edi
    loop dc_cycle
    cmp  edx,0
    jg   dec_cycle
    mov  ecx,[fld_size]
    xor  edx,edx
  fp_cycle:
    mov  al,[field+edx]
    and  al,0xfe
    cmp  al,tPlayer
    je   pl_found
    inc  edx
    loop fp_cycle
  pl_found:
    mov  [player],dx
    movzx eax,byte[checkcount]
;    debug_print_dec eax
    ret

;   *********************************************
;   * WHETHER OBJECT IS VICTORY DEPENDENT *******
;   *********************************************

chk_win_obj:             ; al-object in a cell
    push ecx eax
    and  al,0xf
    mov  cl,[workmode]
    cmp  cl,sSokoban
    jne  nota_sokoban
    cmp  al,tBlock
    jmp  cwo_exit
  nota_sokoban:
    cmp  cl,sSokonex
    jne  nota_sokonex
    cmp  al,tConnect
    je   cwo_exit
    cmp  al,tStConnect
    jmp  cwo_exit
  nota_sokonex:
    push eax
    and  eax,tRedB
    cmp  eax,tRedB
    pop  eax
  cwo_exit:
    pop  eax ecx
    ret

;   *********************************************
;   ***** GET CELL AT CERTAIN DIRECTION *********
;   *********************************************

get_cell_at:            ; in:  dx - current cell, cl - direction
    mov  ebx,edx        ; out: al - object at direction, bx - new position
    movzx eax,cl
    and  eax,11b
    mov  eax, [move_map+eax*4]
    add  ebx,eax
    mov  al,[field+ebx]
    ret

;   *********************************************
;   *** WHETHER A MOVE CAN BE DONE, & DO IT *****
;   *********************************************

valid_move:                         ; in:  dx - current cell, cl - direction
    push edx esi
    call get_cell_at                ; if ch>0 perform all moves
    cmp  al,tWall
    jb   result_ok
    je   vm_exit
    cmp  [workmode],sSokonex
    jne  n_vm_nex
    cmp  al,tStConnect
    je   vm_exit
    cmp  al,tHole
    je   vm_exit
  n_vm_nex:
    push edx ebx
    mov  edx,ebx
    movzx esi,al
    call get_cell_at
    cmp  al,tPlace
    jbe  push_it
    cmp  [workmode],sSokonex
    jne  no_plate
    cmp  al,tHole
    jne  no_plate
    cmp  esi,tBroken
    jae  vm_sink
    cmp  esi,tPlate
    jne  no_plate
    and  byte[field+ebx],0
  vm_sink:
    and  byte[field+edx],0
    jmp  vm_hole
  no_plate:
    pop  ebx edx esi edx
    ret
  push_it:
    call do_move
  vm_hole:
    pop  ebx edx
  result_ok:
    call do_move
    xor  al,al
  vm_exit:
    pop  esi edx
    ret

;   *********************************************
;   ******* ACTUALLY PERFORM MOVES **************
;   *********************************************

do_move:                            ; in: dx - source cell
    test ch,ch                      ;     bx - target cell
    jz   dm_exit                    ;     ch = 0 don't perform moves
    mov  al,byte[field+edx]
    cmp  byte[workmode],sSokoban
    jne  no_dm_ban
    and  al,0xfe
  no_dm_ban:
    xor  byte[field+edx],al
    or   byte[field+ebx],al
    call chk_win_obj
    jne  no_check_win
    pusha
    movzx ecx,byte[checkcount]
    xor  edi,edi
  dm_cycle:
    movzx esi,word[checkpoint+edi*2]
    and  esi,0xfff
    and  edx,0xfff
    cmp  esi,edx
    jnz  not_an_obj
    movzx eax,dl
    movzx eax,byte[field+ebx]
    shl  eax,12
    or   eax,ebx
    mov  word[checkpoint+edi*2],ax
    jmp  dm_ex
  not_an_obj:
    inc  edi
    loop dm_cycle
  dm_ex:
    popa
    call check_win
    jne  no_check_win
    mov  byte[winmode],WM_WINNER
  no_check_win:
    cmp  al,tPlayer
    jne  dm_exit
    mov  [player],bx
  dm_exit:
    ret

;   *********************************************
;   ******* CHECK VICTORY CONDITIONS ************
;   *********************************************

check_win:
;    debug_print <13,10>
    push eax ebx ecx esi
    xor  eax,eax
    movzx ecx,byte[checkcount]
    mov  esi,checkpoint
    mov  bl,byte[workmode]
    xor  bh,bh
    mov  [colcount],bh
    cld
  cw_cycle:
    lodsw
    cmp  bl,sSokoban
    jne  nocw_sokoban
    test ax,1 shl 12
    jz   cw_not_inplace
    inc  bh
  cw_not_inplace:
    loop cw_cycle
;    movzx eax,bh
    cmp  [checkcount],bh
    jmp  cw_exit
  nocw_sokoban:
    cmp  bl,sSokonex
    jne  nocw_sokonex
    mov  dx,ax
    call scan_root
    cmp  al,[checkcount]
    jmp  cw_exit

  nocw_sokonex:
    cmp  esi,checkpoint+8
    ja   cwlor_exit
;    debug_print '*'
    test ax,ax
    jz   cw_cycle
    mov  dx,ax
    call scan_root
    add  [colcount],al
;    debug_print '*->'
;    debug_print_dec eax
    jmp  cw_cycle
  cwlor_exit:
    mov  al,[colcount]
    cmp  al,[checkcount]
  cw_exit:
;    debug_print <13,10>
    pop esi ecx ebx eax
    ret

;   *********************************************
;   **** WHETHER LASERS DESTROY SOMETHING *******
;   *********************************************

check_lasers:
    pusha
    xor  edx,edx
    mov  ecx,[fld_size]
  cl_loop:
    push ecx edx
    mov  cl,[field+edx]
    sub  cl,tLaserW
    jl   cl_exit
  cl_begin:
    call get_cell_at
    cmp  al,tLaserW
    jae  cl_destroy
    cmp  al,tBroken
    je   cl_destroy
    cmp  al,tEmpty
    je   no_cl_destroy
    cmp  al,tHole
    je   no_cl_destroy
    cmp  al,tPlayer
    jne  cl_exit
    mov  ecx,0x00ac0000
    mov  edx,lose_msg
    mov  esi,lose_msg_end-lose_msg  ; print loose message
    mov  byte[winmode],WM_LOSE
    mov  ebx,WIN_XY
    mov  eax,4
    int  0x40
    jmp  cl_exit
  cl_destroy:
    mov  byte[field+ebx],0
  no_cl_destroy:
    mov  edx,ebx
    jmp  cl_begin
  cl_exit:
    pop  edx ecx
    inc  edx
    loop cl_loop
    popa
    ret

;   *********************************************
;   *** USED BY CHECK_WIN IN SOKONEX & SOKOLOR **
;   *********************************************

scan_root:                ; input:   dx-beginning cell, ebx-what to search
    push esi
    mov  edi,srch         ; output:  eax-number of win_obj found
    mov  eax,0xfff
    movzx ecx,[checkcount]
    inc  ecx
    cld
    rep  stosw            ; clearing area for scan
    movzx ebx,dx
    and  edx,eax          ; dx-cell number to compare with
    shr  ebx,12           ; bl-obj id
    mov  [color],bl
    mov  esi,srch
    mov  edi,eax          ; mask to extract cell
    mov  word[srch],dx
  sr_loop:
    lodsw
    push esi              ; saving scan pointer
       movzx edx,ax       ; edx-[dirs*4][cell*12]
       and  edx,edi
;       debug_print ' >'
       mov  ecx,4
  sr_dir_loop1:
;       debug_print '.'
       push ecx           ; saving dir counter
         lea  ebx,[ecx+11]
         bts  word[esi-2],bx
         jc   sr_endloop      ; this entry is already processed
;         debug_print '^'
         dec  ecx             ; cl-direction
         call get_cell_at     ; bx-new position, al-object
;         cmp  [workmode],sSokonex
;         jne  no_sr_nex
         call chk_win_obj
         jne  sr_endloop      ; not a win_obj there
;         debug_print '@'
         cmp  [workmode],sSokolor
         jne  no_sr_lor
         cmp  al,[color]
         jne  sr_endloop
       no_sr_lor:
         push esi
         mov  esi,srch        ; let us search for existing entries
       sr_loop1:
         lodsw
         and  eax,edi         ; eax-cell w/o dirs
         cmp  eax,ebx
         je   sr_foundentry   ; this is the entry we're seeking for
         cmp  word[esi],0xfff
         jnz  sr_loop1        ; next entry
    ; we reached empty area
         mov  [esi],bx
         add  esi,2
       sr_foundentry:
         mov  eax,15
         sub  eax,ecx
         bts  [esi-2],ax      ; mark entry as used
         pop  esi
;         inc  [e_fnd]         ; one more obj found
     sr_endloop:
       pop  ecx
       loop sr_dir_loop1
;       jmp  tttt
;     sr_dir_loop:
;       jmp  sr_dir_loop1
;     tttt:
    pop esi
    cmp  word[esi],0xfff
    jne  sr_loop
    mov  eax,esi
    sub  eax,srch
    shr  eax,1
    pop  esi
;    debug_print_dec eax
    ret

;   *********************************************
;   *** SPECIAL ROUTINE TO DRAW LASERS **********
;   *********************************************

draw_lasers:
    xor  edx,edx
    mov  ecx,[fld_size]
  dl_loop:
    push ecx edx
    mov  cl,[field+edx]
    sub  cl,tLaserW
    jl   dl_eloop
    inc  ch
  dl_gca:
    call get_cell_at
    cmp  al,tEmpty
    je   dl_draw
    cmp  al,tHole
    jne  dl_eloop
  dl_draw:
    call draw_beams
    mov  edx,ebx
    jmp  dl_gca
  dl_eloop:
    pop  edx
    inc  edx
    pop  ecx
    loop dl_loop
    ret

;   *********************************************
;   *** DRAWS LASER BEAMS IN CERTAIN DIRECTION **
;   *********************************************

draw_beams:
    pusha
    mov esi,[levptr]
    movzx esi,byte[esi+1]
    mov  eax,ebx
    xor  edx,edx
    div  esi
    movzx esi,cl
    dec  esi
    shr  esi,1
    and  esi,1
    shl  edx,20
    mov  ebx,edx
    shl  eax,20
    mov  ecx,eax
    add  ebx,dword[beam_xy+esi*8]
    add  ecx,dword[beam_xy+esi*8+4]
    mov  edx,0xe9e25c
    mov  eax,13
    int  0x40
    popa
    ret

ud:
   ud2  ; debugging purposes only


;   *********************************************
;   *** COMPRESS LEVEL - NOT READY YET **********
;   *********************************************

;    push    esi ebx ;ecx
;    xchg    ebx,edi
;    mov     esi,edi                 ; esi,edi - beginning
;; ebx - end of unpacked field
;  first_enc:
;    lodsb                           ; al - first byte
;    shl     ax,8                    ; ah - this byte, al=0
;  next_enc:
;    cmp     esi,ebx
;    jae     exit_enc
;;    movzx   ecx,byte[esi]
;;    debug_print_dec ecx
;    cmp     ah,byte[esi]
;    jne     newchar
;    inc     esi
;    inc     al
;    cmp     al,15
;    jb      next_enc
;  newchar:
;    shl     al,4
;    shr     ax,4
;    stosb
;    jmp     first_enc
;  exit_enc:
;    shl     al,4
;    shr     ax,4
;    stosb
;    mov     al,0xff
;    stosb
;    pop     ebx esi ecx
;
;    dec     ecx
;    jcxz    outcycle
;    jmp     next_lev
;  outcycle:


; ‡¤¥áì ­ å®¤ïâáï ¤ ­­ë¥ ¯à®£à ¬¬ë:

; ¨­â¥àä¥©á ¯à®£à ¬¬ë ¤¢ãï§ëç­ë© - § ¤ ©â¥ ï§ëª ¢ macros.inc
load_char:
if lang eq ru
     db '‡ £à㧨âì'
else
     db 'Open file'
end if
loadlen:

ll_msg:
if lang eq ru
     db '‚ë¡¥à¨â¥ ã஢¥­ì'
else
     db 'Choose a level'
end if
     db ' (0-9, PgUp, PgDn)'
ll_msg_end:

fn_input:
;   db 'cnf'
;    db 'soko-4.lev'
if lang eq ru
     db '¨«¨ ¢¢¥¤¨â¥ ¨¬ï ä ©« '
else
     db 'or enter a filename'
end if
inp_end:
     rb 256-(inp_end-fn_input)

win_msg:
if lang eq ru
     db '“à !!! ‚ë ¯à®è«¨ ã஢¥­ì!'
else
     db "You've completed the level!"
end if
win_msg_end:

lose_msg:
if lang eq ru
     db '‚ë ¯ à «¨§®¢ ­ë! à®¨£àëè...'
else
     db "You're paralized! Game over..."
end if
lose_msg_end:

zagolovok:
     db   'Sokoban', 0


pic_map:
    db 0xf,9,0,0,1,1,5,6
pm_nex:
    db 2,7,8,3,4,0xa,0xa,0xa,0xa
pm_col:
    db 0xb,0xc,0xd,0xe

beam_xy:
    dd (FLD_LEFT+7) shl 16+2, FLD_TOP2+16
    dd FLD_LEFT2+16, (FLD_TOP+7) shl 16+2

ll_num db '00x.'

move_map dd -1,+0,-0,1      ; 0 - W, 1 - S, 2 - N, 3 - E

stdlev     db 'SOKO-0.LEV',0
stdlev_len:

inp_pos    dd inp_end-fn_input
entered    dd 0

file_info:
           dd 0                 ; subfunction - read
           dd 0, 0              ; file offset
           dd 0x20000           ; number of bytes to read
cnf_level  dd strip             ; data buffer
file_name  db CUR_DIR           ; Filename
path_end   db 'SKIN.'
file_num   db 'RAW',0

           rb 256-($-file_name)

I_END:  ; ª®­¥æ ¯à®£à ¬¬ë

winmode         db ?
scanptr         dd ?
levpage         dd ?
workmode        db ?
player          dw ?
fld_size        dd ?
levptr          dd ?
wnd_height      dd ?
wnd_width       dd ?
color           db ?
colcount        db ?
levelcount      dd ?
checkcount      db ?
checkpoint      rw 256
levelmap        rd 1024
strip           rb SKIN_SIZE

workarea:
  srch  rb 0x10000-($-workarea)

level_start rb 0x20000
field: