' ----------------------------------------------------------------
' This file is released under the MIT License
'
' Copyleft (k) 2008
' by Jose Rodriguez-Rosa (a.k.a. Boriel) <http://www.boriel.com>
' Contributed by @Winston, @Guesser, @Ardentcrest
'
' Thanks a lot for your help! :))
' ----------------------------------------------------------------

#ifndef __LIBRARY_SPECTRANET__

REM Avoid recursive / multiple inclusion

#define __LIBRARY_SPECTRANET__
#pragma push(case_insensitive)
#pragma case_insensitive = TRUE

#include <string.bas>


' POSIX file flags
#define O_RDONLY        $0001  'Open read only
#define O_WRONLY        $0002  'Open write only
#define O_RDWR          $0003  'Open read/write
#define O_APPEND        $0008  'Append to the file, if it exists (write only)
#define O_CREAT         $0100  'Create the file if it doesn't exist (write only)
#define O_TRUNC         $0200  'Truncate the file on open for writing
#define O_EXCL          $0400  'With O_CREAT, returns an error if the file exists


' CHMOD POSIX file Mode
#define SISUID   04000o   'set user ID on execution
#define SISGID   02000o   'set group ID on execution
#define SISVTX   01000o   'sticky bit
#define SIRUSR   00400o   'read by owner
#define SIWUSR   00200o   'write by owner
#define SIXUSR   00100o   'execute/search by owner
#define SIRGRP   00040o   'read by group
#define SIWGRP   00020o   'write by group
#define SIXGRP   00010o   'execute/search by group
#define SIROTH   00004o   'read by others
#define SIWOTH   00002o   'write by others
#define SIXOTH   00001o   'execute/search by others


#define SEEK_SET 0x00
#define SEEK_CUR 0x01
#define SEEK_END 0x02


' Macro to convert a string to ASCIIZ
#define ASCIIZ(x)   (x + CHR$(0))
' Where to store FILE op errors
#define ERR_NR 23610


Function FASTCALL SNETsocket(stype As ubyte) As byte
    Asm
    push namespace core
    ld c, a  ; c = type
    ld hl, Spectranet.SOCKET
    call Spectranet.HLCALL
    pop namespace
    End Asm
End Function


Function FASTCALL SNETbind(socket as ubyte, port As uinteger) As byte
    Asm
    push namespace core
    pop hl
    ex (sp), hl
    ex de, hl  ; HL = port
    ld hl, Spectranet.BIND
    call Spectranet.HLCALL
    pop namespace
    End Asm
End Function


Function FASTCALL SNETlisten(socket As ubyte) As byte
    Asm
    push namespace core
    ld hl, Spectranet.LISTEN
    call Spectranet.HLCALL
    pop namespace
    End Asm
End Function


Function FASTCALL SNETaccept(socket As ubyte) As byte
    Asm
    push namespace core
    ld hl, Spectranet.ACCEPT
    call Spectranet.HLCALL
    pop namespace
    End Asm
End Function


Function FASTCALL SNETconnect(socket As ubyte, ip$, port As uinteger) As byte
    Asm
    push namespace core
    pop hl  ; ret address
    pop de  ; string containing the IP in CODE format. e.g. 10.0.0.1 = chr$(10, 0, 0, 1)
    pop bc  ; port
    push hl  ; Ret address restored; hl = port
    ld hl, Spectranet.CONNECT
    push de ; Used later to free the string
    inc de
    inc de
    call Spectranet.HLCALL
    pop hl
    ex af, af'
    call __MEM_FREE
    ex af, af'
    pop namespace
    End Asm
End Function


Function FASTCALL SNETclose(socket As ubyte) As byte
    Asm
    push namespace core
    ld hl, Spectranet.CLOSE
    call Spectranet.HLCALL
    pop namespace
    End Asm
End Function


Function FASTCALL SNETrecv(socket As ubyte, addr As uinteger, length as uinteger) as byte
    Asm
    push namespace core
    pop hl  ; Ret address
    pop de  ; address
    pop bc  ; Length
    push hl ; Restore ret address
    ld hl, Spectranet.RECV
    call Spectranet.HLCALL
    pop namespace
    End Asm
End Function


Function FASTCALL SNETsend(socket As ubyte, addr As uinteger, length as uinteger) as byte
    Asm
    push namespace core
    pop hl  ; Ret address
    pop de  ; address
    pop bc  ; Length
    push hl ; Restore ret address
    ld hl, Spectranet.SEND
    call Spectranet.HLCALL
    pop namespace
    End Asm
End Function


Function FASTCALL SNETpollfd(socket As ubyte) as ubyte
    Asm
    push namespace core
    ld hl, Spectranet.POLLFD
    call Spectranet.HLCALL
    ld a, 0
    ret z
    ld c, a
    pop namespace
    End Asm
End Function


' -----------------------------------------------------------
' Reads a memory address (16 bit) from paged memory
' -----------------------------------------------------------
Function FASTCALL SNETpeekUinteger(addr as Uinteger) As UInteger
    Asm
    push namespace core
    ex de, hl
    ld hl, Spectranet.PAGEIN
    call Spectranet.HLCALL
    ex de, hl
    ld e, (hl)
    inc hl
    ld d, (hl)
    ld hl, Spectranet.PAGEOUT
    call Spectranet.HLCALL
    ex de, hl
    pop namespace
    End Asm
End Function


' -----------------------------------------------------------
' Filesystem functions (fopen, fclose, fread, fwrite, fseek)
' -----------------------------------------------------------

' -----------------------------------------------------------
' Mounts a remote filesystem. Returns session number.
'
' Example:
'     SNETmount(0, "tnfs", "vexed4.alioth.net", "", "", "")
' -----------------------------------------------------------
Function SNETmount(mpoint as Ubyte, proto$, host$, source$, userid$, passwd$) As Integer
    REM Convert the functions to ASCIIZ
    proto$  = ASCIIZ(proto$)
    host$   = ASCIIZ(host$)
    source$ = ASCIIZ(source$)
    userid$ = ASCIIZ(userid$)
    passwd$ = ASCIIZ(passwd$)

    DIM buffer(4) as Uinteger
    DIM ix as UInteger

    buffer(0) = PEEK(Uinteger, @proto) + 2
    buffer(1) = PEEK(Uinteger, @host) + 2
    buffer(2) = PEEK(Uinteger, @source) + 2
    buffer(3) = PEEK(Uinteger, @userid) + 2
    buffer(4) = PEEK(Uinteger, @passwd) + 2

    ix = @buffer(0)  ' Useless, but will allow to calculate HL
    Asm
    push namespace core
    ld a, (ix + 5)
    push ix     ; Must be restored upon return
    push hl
    pop ix
    ld hl, Spectranet.MOUNT
    call Spectranet.HLCALL
    pop ix
    pop namespace
    End Asm
End Function


' -----------------------------------------------------------
' Returns current mount point
' -----------------------------------------------------------
Function SNETcurrMPoint() as Byte
    print inK 7; paper 2; CAST(Ubyte, SNETpeekUinteger(0x1001))
    return SNETpeekUinteger(0x1001)
End Function


' -----------------------------------------------------------
' Changes the current mount point
' -----------------------------------------------------------
sub FASTCALL SNETsetmountpt(mpoint)
    Asm
    push namespace core
    ld hl, Spectranet.SETMOUNTPOINT
    call Spectranet.HLCALL
    pop namespace
    End Asm
End sub


' -----------------------------------------------------------
' Umounts a previously mounted filesystem
'
' Example:
'     SNETumount(0)
' -----------------------------------------------------------
Function FASTCALL SNETumount(mpoint as UByte) As UInteger
    Asm
    push namespace core
    ld hl, Spectranet.UMOUNT
    call Spectranet.HLCALL
    pop namespace
    End Asm
End Function


' -----------------------------------------------------------
' Opens a file and returns its handle. -1 on Error
' This function will be changed to copycat the C style,
' like: SNETfopen(0, "myfile", "rb"). Not yet.
' Returns the file handle (byte). -1 On error.
'
' Example:
'    Opens a file for read. The 1st one is the mount point.
'    The last parameter is ignored (0)
'    fhandle = SNETfopen(0, "myfile.bin", O_RDONLY, 0)
'
'    Opens a file for writing (creates the file). The last parameter
'    is the chmod.
'    fhandle = SNETopen(0, "newfile.blah", O_CREAT | O_WRONLY, 0666o)
' -----------------------------------------------------------
Function SNETopen(mpoint as Ubyte, fname$, flags as UInteger, chmod as Uinteger) As Byte
    DIM addrOfFname as Uinteger
    fname$ = ASCIIZ(fname$)
    addrOfFname = PEEK(Uinteger, @fname$) + 2
    Asm
    push namespace core
    ld a, (ix + 5)      ; mount point
    ld e, (ix + 8)
    ld d, (ix + 9)      ; de = flags (ORDONLY, etc...)
    ld c, (ix + 10)
    ld b, (ix + 11)     ; bc = chmod mode

    push ix
    ld ix, Spectranet.OPEN
    call Spectranet.IXCALL
    pop ix
    pop namespace
    End Asm
End Function


' -----------------------------------------------------------
' Reads content from a file, an places it at a memory address.
' Returns the effectively number of bytes read.
'
' Example:
'    Loading a binary screen (no .TAP file, just raw bytes)
'    SNETfread(f, 16384, 6912)
' -----------------------------------------------------------
Function FASTCALL SNETfread(fhandle as Ubyte, addr as Uinteger, size as Uinteger) As Uinteger
    Asm
    push namespace core
    pop hl    ; ret address
    pop de
    pop bc
    push hl
    ld hl, Spectranet.READ
    call Spectranet.HLCALL
    ld h, b   ; BC = Num. of bytes read if no Carry
    ld c, l
    ret nc
    ld (ERR_NR), a
    pop namespace
    End Asm
End Function


' -----------------------------------------------------------
' Write content from a memory address into a file
' Returns the effectively number of bytes written.
'
' Example:
'    Saving screen into a binary file (no .TAP file, just raw bytes)
'    SNETfwrite(f, 16384, 6912)
' -----------------------------------------------------------
Function FASTCALL SNETfwrite(fhandle as Ubyte, addr as Uinteger, size as Uinteger) As Uinteger
    Asm
    push namespace core
    pop hl    ; ret address
    pop de    ; addr
    pop bc    ; size
    push hl   ; ret address
    ex de, hl ; HL = address to write
    push ix
    ld ix, Spectranet.WRITE
    call Spectranet.IXCALL
    pop ix
    ld h, b
    ld c, l
    ret nc
    ld (ERR_NR), a
    pop namespace
    End Asm
End Function



' -----------------------------------------------------------
' Mimics C fopen. Opens a file for reading or writing,
' returning its handle on success or -1 on error.
' -----------------------------------------------------------
Function SNETfopen(mpoint as Ubyte, fname$, mode$) as Byte
    dim flags As Uinteger = 0
    if inStr(mode$, "r")
        flags = flags | O_RDONLY
    end if
    if inStr(mode$, "w")
        flags = flags | O_CREAT | O_TRUNC | O_WRONLY
    end if
    if inStr(mode$, "a")
        flags = flags | O_APPEND | O_CREAT | O_WRONLY
    end if
    if inStr(mode$, "+")
        flags = flags | O_RDWR
    end if

    return SNETopen(mpoint, fname$, flags, 666o)

End Function


' -----------------------------------------------------------
' Closes an open file.
'
' Example:
'    SNETfclose(f)
' -----------------------------------------------------------
Function FASTCALL SNETfclose(fhandle as Ubyte) As Byte
    Asm
    push namespace core
    ld hl, Spectranet.CLOSE
    call Spectranet.HLCALL
    ret c
    xor a     ; Ensures A = 0 on success
    pop namespace
    End Asm
End Function


' -----------------------------------------------------------
' Changes the current file read/write position.
'
' Example:
'      - Jumps to the end of file
'     SNETfseek(f, SEEK_END, 0)
' -----------------------------------------------------------
Function FASTCALL SNETfseek(fhandle as Ubyte, op as Ubyte, pos as ULong) as Byte
    Asm
    push namespace core
    pop hl  ;  Return address
    ; pop af ; Not done. FASTCALL passes always the 1s parameter
    pop bc  ;  Bytes comes in the high part, so B.
    ld c, b ;  C = operation
    pop de  ;  Low Ulong 32
    ex (sp), hl  ; Push ret address back, hl = high part
    ex de, hl  ; Now DEHL = Ulong 32
    push ix
    ld ix, Spectranet.LSEEK
    call Spectranet.IXCALL
    pop ix
    ret c
    xor a      ; Ensures A = 0 on success
    pop namespace
    End Asm
End function


' -----------------------------------------------------------
' Deletes a file.
'
' Example:
'     SNETunlink("myfile.dat")
' -----------------------------------------------------------
Function SNETunlink(fname$) AS Byte
    fname$ = ASCIIZ(fname$)
    DIM addr as Uinteger
    addr = PEEK(Uinteger, @fname) + 2
    Asm
    push namespace core
    PROC
    LOCAL CONT
    push ix
    ld ix, Spectranet.UNLINK
    call Spectranet.IXCALL
    pop ix
    jr c, CONT
    xor a      ; Ensures A = 0 on success
    CONT:
    ENDP
    pop namespace
    End Asm
End Function


#undef ASCIIZ
#undef ERR_NR

#require "spectranet.inc"
#require "free.asm"

#pragma pop(case_insensitive)
#endif
