;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                             ;
;    Tiny MP3 Shoutcast Server v0.1 (vt)      ;
;                                             ;
;    Compile with FASM for Menuet             ;
;                                             ;
;    Listening to port 8008                   ;
;    Connect with eg: 192.168.1.22:8008       ;
;                                             ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

version  equ  '0.3'

  use32
  org     0x0
  db      'MENUET01'    ; 8 byte id
  dd      0x01          ; header version
  dd      START         ; program start
  dd      I_END         ; program image size
  dd      0x80000       ; memory usage
  dd      0x20000       ; stack
  dd      0,0

include 'lang.inc'
include 'macros.inc'

; 0x0+      program image
; 0x1ffff   stack
; 0x20000   work area for file read
; 0x40000+  file send buffer ( 100 kb )


START:                          ; start of execution

    mov  [status],0
    call clear_input
    call draw_window            ; at first, draw the window

still:

    mov  eax,23                 ; wait here for event
    mov  ebx,2
    int  0x40

    call check_events

    call check_connection_status

    cmp  [status],4
    je   start_transmission

    jmp  still


check_events:

    cmp  eax,1                  ; redraw request ?
    jz   red
    cmp  eax,2                  ; key in buffer ?
    jz   key
    cmp  eax,3                  ; button in buffer ?
    jz   button

    ret

red:                           ; redraw
    call draw_window
    ret

key:
    mov  eax,2                 ; Just read it and ignore
    int  0x40
    ret

button:                         ; button

    mov  eax,17                 ; get id
    int  0x40

    cmp  ah,1                   ; close
    jne  no_close
    mov  eax,-1
    int  0x40
  no_close:

    cmp  ah,2                   ; button id=2 ?
    jnz  tst3
    ; open socket
    mov  eax,53
    mov  ebx,5
    mov  ecx,8008   ; local port # - http
    mov  edx,0      ; no remote port specified
    mov  esi,0      ; no remote ip specified
    mov  edi,0      ; PASSIVE open
    int  0x40
    mov  [socket], eax
    mov  [posy],1
    mov  [posx],0
    mov  [read_on],1
    call check_for_incoming_data
    call draw_window
    ret
  tst3:

    cmp  ah,4
    je   close_socket
    cmp  ah,6
    je   close_socket
    jmp  no_socket_close
  close_socket:
    mov  edx,eax
    ; Close socket
    mov  eax, 53
    mov  ebx, 8
    mov  ecx, [socket]
    int  0x40
    mov  esp,0x1fff0
    cmp  dh,6
    je   read_string
    jmp  still
  no_socket_close:

    cmp  ah,9
    jne  no_bps_add
    add  [bps],8*1000
    call draw_window
    ret
  no_bps_add:

    cmp  ah,8
    jne  no_bps_sub
    sub  [bps],8*1000
    call draw_window
    ret
  no_bps_sub:


    ret


clear_input:

    mov  edi,input_text
    mov  eax,0
    mov  ecx,60*40
    cld
    rep  stosb

    ret


read_string:

    mov  [addr],dword filename
    mov  [ya],dword 95

    mov  edi,[addr]
    mov  eax,32
    mov  ecx,30
    cld
    rep  stosb

    call print_text

    mov  edi,[addr]

  f11:
    mov  eax,10
    int  0x40
    cmp  eax,2
    jne  read_done
    mov  eax,2
    int  0x40
    shr  eax,8
    cmp  eax,13
    je   read_done
    cmp  eax,8
    jnz  nobsl
    cmp  edi,[addr]
    jz   f11
    sub  edi,1
    mov  [edi],byte 32
    call print_text
    jmp  f11
  nobsl:
    cmp  eax,dword 31
    jbe  f11
    cmp  eax,dword 95
    jb   keyok
    sub  eax,32
  keyok:
    mov  [edi],al

    call print_text

    add  edi,1
    mov  esi,[addr]
    add  esi,30
    cmp  esi,edi
    jnz  f11

  read_done:

    mov  ecx,40
    mov  eax,0
    cld
    rep  movsb

    call print_text

    jmp  still


print_text:

    pusha

    mov  eax,13
    mov  ebx,56*65536+30*6
    mov  ecx,[ya]
    shl  ecx,16
    mov  cx,8
    mov  edx,0xffffff
    int  0x40

    mov  eax,4
    mov  edx,[addr]
    mov  ebx,56*65536
    add  ebx,[ya]
    mov  ecx,0x000000
    mov  esi,30
    int  0x40

    popa
    ret


wait_for  dd 0x0

transmission_start  dd  0x0
sentbytes           dd  0x0

start_transmission:

    call clear_input

    mov  eax,5
    mov  ebx,50
    int  0x40

    call check_for_incoming_data
    call draw_window

    call send_header

    mov  [fileinfo+4],dword 0   ; start from beginning
    mov  [read_to],0x40000
    mov  [playpos],0x40000

    mov  ecx,1024 / 512

  new_buffer:

    mov  eax,[read_to]
    mov  ebx,1
    call read_file

    loop new_buffer


  newpart:

    call check_connection_status
    call draw_window

    mov  eax,26
    mov  ebx,9
    int  0x40
    mov  [transmission_start],eax
    mov  [sentbytes],0

  newblock:

    mov  eax,[read_to]
    mov  ebx,2
    call read_file

  wait_more:

    mov  eax,26
    mov  ebx,9
    int  0x40

    cmp  eax,[wait_for]
    jge  nomw

    mov  eax,5
    mov  ebx,1
    int  0x40

    jmp  wait_more

  nomw:

    add  eax,2
    mov  [wait_for],eax

    mov  eax,11
    int  0x40
    call check_events

    mov  eax,53
    mov  ebx,255
    mov  ecx,103
    int  0x40

    cmp  eax,0
    jne  wait_more

    ; write to socket
    mov  eax,53
    mov  ebx,7
    mov  ecx,[socket]
    mov  edx,[playadd]
    mov  esi,[playpos]
    int  0x40

    add  [sentbytes],edx

    mov  esi,[playpos]
    add  esi,[playadd]
    mov  edi,0x40000
    mov  ecx,110000 / 4
    cld
    rep  movsd

    mov  eax,[playadd]
    sub  [read_to],eax

    call check_for_incoming_data
    call show_progress
    call check_rate

    mov  eax, 53
    mov  ebx, 6
    mov  ecx, [socket]
    int  0x40
    cmp  eax,4
    jne  end_stream

    cmp  [read_to],0x40000
    jge  newblock

  end_stream:

    ; Close socket

    mov  eax, 53
    mov  ebx, 8
    mov  ecx, [socket]
    int  0x40

    mov  eax,5
    mov  ebx,5
    int  0x40

    ; Open socket

    mov  eax,53
    mov  ebx,5
    mov  ecx,8008   ; local port # - http
    mov  edx,0      ; no remote port specified
    mov  esi,0      ; no remote ip specified
    mov  edi,0      ; PASSIVE open
    int  0x40
    mov  [socket], eax
    mov  [posy],1
    mov  [posx],0
    mov  [read_on],0

    call draw_window

    jmp  still


check_rate:

    pusha

    mov  eax,[bps]
    xor  edx,edx
    mov  ebx,8*100
    div  ebx
    shl  eax,1
    mov  [playadd],eax

    mov  eax,26
    mov  ebx,9
    int  0x40

    sub  eax,[transmission_start]
    shr  eax,1

    imul eax,[playadd]

    mov  edx,0x00dd00

    cmp  [sentbytes],eax
    jge  sendok

    sub  eax,20000
    cmp  [sentbytes],eax        ; a long buffer underrun correction
    jge  no_buffer_overrun      ; actually leads to overrun
    mov  [sentbytes],eax
  no_buffer_overrun:

    add  [playadd],150
    mov  edx,0xdd0000

  sendok:

    mov  eax,13
    mov  ebx,320*65536+10
    mov  ecx,105*65536+10
    int  0x40

    mov  eax,47
    mov  ebx,4*65536
    mov  ecx,[playadd]
    mov  edx,322*65536+106
    mov  esi,0x000000
;    int  0x40

    popa

    ret


show_progress:

    pusha

    mov  eax,13
    mov  ebx,236*65536+10*6
    mov  ecx,107*65536+8
    mov  edx,0xffffff
    int  0x40

    mov  ecx,[fileinfo+4]
    imul ecx,512

    mov  eax,47               ; file read
    mov  ebx,9*65536
    mov  edx,236*65536+107
    mov  esi,0x000000
    int  0x40

    popa
    ret


playpos  dd  0x100000
playadd  dd  256000 / 8 / 100


send_header:

    pusha

    mov   [playpos],0x40000

    mov   esi,fileinfo+5*4
    mov   edi,transname
    mov   ecx,30
    cld
    rep   movsb

    mov   eax, 53
    mov   ebx, 7
    mov   ecx, [socket]
    mov   edx, headere-headers
    mov   esi, headers
    int   0x40

    popa
    ret


read_file:

    cmp  [read_to],0x40000+2000
    jg   cache_ok
    mov  [read_on],1
  cache_ok:

    cmp  [read_to],0x40000+95500
    jg   no_read_1

    mov  [fileinfo+12],eax
    mov  [fileinfo+8],ebx

    mov  eax,58
    mov  ebx,fileinfo
    int  0x40

    cmp  eax,0
    jne  no_read_1

    mov  eax,[fileinfo+8]
    add  [fileinfo+4],eax

    add  [read_to],512*2

    ret

  no_read_1:

    mov  [read_on],0

    ret



check_for_incoming_data:

    pusha

    mov  eax, 53
    mov  ebx, 2
    mov  ecx, [socket]
    int  0x40

    cmp  eax,0
    je   _ret_now

  new_data:

    mov  eax, 53
    mov  ebx, 2
    mov  ecx, [socket]
    int  0x40

    cmp  eax,0
    je   _ret

    mov  eax,53
    mov  ebx,3
    mov  ecx,[socket]
    int  0x40

    cmp  bl,10
    jne  no_lf
    inc  [posy]
    mov  [posx],0
    jmp  new_data
  no_lf:

    cmp  bl,20
    jb   new_data

    inc  [posx]
    cmp  [posx],60
    jbe  xok
    inc  [posy]
    mov  [posx],0
  xok:

    cmp  [posy],12
    jbe  yok
    mov  [posy],1
  yok:

    mov  eax,[posy]
    imul eax,60
    add  eax,[posx]

    mov  [input_text+eax],bl

    jmp  new_data

  _ret:

;    call draw_window

  _ret_now:

    popa
    ret



check_connection_status:

    pusha

    mov  eax, 53
    mov  ebx, 6
    mov  ecx, [socket]
    int  0x40

    cmp  eax,[status]
    je   .ccs_ret
    mov  [status],eax
    add  eax,48
    mov  [text+20],al
    call draw_window
   .ccs_ret:

    popa
    ret




;   *********************************************
;   *******  WINDOW DEFINITIONS AND DRAW ********
;   *********************************************


draw_window:

    pusha

    mov  eax,12                    ; function 12:tell os about windowdraw
    mov  ebx,1                     ; 1, start of draw
    int  0x40

    mov  eax,0                     ; Draw Window
    mov  ebx,50*65536+410
    mov  ecx,100*65536+141
    mov  edx,0x03ffffff
    mov  esi,0x00405080
    mov  edi,0x00405080
    int  0x40

    mov  eax,4                     ; Window label
    mov  ebx,8*65536+8
    mov  ecx,0x10ffffff
    mov  edx,wls
    mov  esi,wle-wls
    int  0x40

    mov  eax,8                     ; Start server
    mov  ebx,(25)*65536+21
    mov  ecx,57*65536+10
    mov  edx,2
    mov  esi,0x409040
    int  0x40                      ; Stop server
    mov  eax,8
    mov  ebx,(25)*65536+21
    mov  ecx,69*65536+10
    mov  edx,4
    mov  esi,0x904040
    int  0x40

    mov  esi,0x3366d0

    mov  eax,8                     ; Enter filename
    mov  ebx,(25)*65536+21
    mov  ecx,93*65536+10
    mov  edx,6
    int  0x40
    mov  eax,8                     ; Decrease transfer rate
    mov  ebx,(25)*65536+10
    mov  ecx,105*65536+10
    mov  edx,8
    int  0x40
    mov  eax,8                     ; Increase transfer rate
    mov  ebx,(36)*65536+10
    mov  ecx,105*65536+10
    mov  edx,9
    int  0x40

    mov  ebx,10*65536+35           ; draw info text
    mov  ecx,0x00000000
    mov  edx,text
    mov  esi,40
  newline:
    mov  eax,4
    int  0x40
    add  ebx,12
    add  edx,40
    cmp  [edx],byte 'x'
    jnz  newline

    mov  eax,4                     ; Filename
    mov  ebx,56*65536+95
    mov  ecx,0x000000
    mov  edx,filename
    mov  esi,30
    int  0x40

    mov  eax,[bps]
    xor  edx,edx
    mov  ebx,1000
    div  ebx
    mov  ecx,eax

    mov  eax,47
    mov  ebx,3*65536
    mov  edx,58*65536+107
    mov  esi,0x00000000
    int  0x40

    mov  [input_text+0],dword 'RECE'
    mov  [input_text+4],dword 'IVED'
    mov  [input_text+8],dword ':   '

    mov  ebx,230*65536+35           ; draw info text
    mov  ecx,0x00000000
    mov  edx,input_text
    mov  esi,28
    mov  edi,7
   newline2:
    mov  eax,4
    int  0x40
    add  ebx,10
    add  edx,60
    dec  edi
    jnz  newline2

    mov  eax,38
    mov  ebx,210*65536+210
    mov  ecx,22*65536+136
    mov  edx,0x6699cc ; 002288
    int  0x40

    mov  eax,38
    mov  ebx,211*65536+211
    mov  ecx,22*65536+136
    mov  edx,0x336699 ; 002288
    int  0x40

    mov  eax,12                    ; function 12:tell os about windowdraw
    mov  ebx,2                     ; 2, end of draw
    int  0x40

    popa

    ret



; DATA AREA

text:
   db '        TCB status: 0                   '
   db '                                        '
   db '        Activate - port 8008            '
   db '        Stop server                     '
   db '                                        '
   db '    >                                   '
   db '   < >      Kbps                        '
   db 'x';  <- END MARKER, DONT DELETE

headers:

  db   'ICY 200 OK',13,10
  db   'icy-notice1:This stream requires Winamp or xmms',13,10
  db   'icy-url:http://www.menuetos.org',13,10
  db   'icy-pub: 1',13,10
  db   'icy-name: Menuet Mp3 Shoutcast Radio ',version,' - '
 transname:
  db   '                              ',13,10,13,10

headere:

wls:  db   'MP3 shoutcast server ',version
wle:

socket   dd  0
status   dd  0

posy     dd  1
posx     dd  0

read_on  db  1
read_to  dd  0

addr     dd  0
ya       dd  0

bps      dd  128*1000

fileinfo:  dd  0,0,0,0,0x20000
filename:  db  '/RD/1/MENUET.MP3',0
times 50   db  0

input_text:

I_END:
