Metropoli BBS
VIEWER: pack.asm MODE: TEXT (LATIN1)
;this is part of file i/o
;for packed file loading/etc.

.data?

align 4
pts dd ?  ;table size
phs dd ?  ;handles size

old_int21h df ? ;old int21h handler
coff dd ?  ;current file offset (within the pack file)
csiz dd ?  ;current file size
cpos dd ?  ;current file position (0=starting pos)
cend dd ?  ;coff + csiz

fn db 13 dup (?)    ;File name used by _open

ps struct
  fn db 13 dup (?)   ;the name
  fh dw ?            ;the handle (not used in the header when in the file)
  off dd ?           ;offset in the file
  siz dd ?           ;size of file
ps ends

.data
pt dd 0   ;the tables  (malloc'ed)
  ;this holds the struct of each file loaded (within the pack files)
ph dd 0   ;Packs Handles (malloc'ed) FIX : v2.01 : No longer restricted to 16
  ;this holds the handles of each pack file loaded to quickly know when
  ; file IO is being performed on a pack file

pack_on db 0  ;is packed file init?
chand dw 0  ;current handle (0=none!)  (if one file is opened then
            ; another cannot be opened!)
temp4 dd 0   ;used all over
noff dd 0    ;# of files (this must come right after temp4)

.code

readpack proc private,h:word,buf:dword,len:dword
  mov ah,3fh
  mov bx,h
  mov ecx,len
  mov edx,buf
  pushfd
  call [old_int21h]
  ret
readpack endp

closepack proc private,h:word
  mov bx,h
  mov ah,3eh
  pushfd
  call [old_int21h]
  ret
closepack endp

openpack proc private,nam:dword,acc:byte
  mov edx,nam
  mov ah,3dh
  mov al,acc
  pushfd
  call [old_int21h]
  ret
openpack endp

pack_init proc,siz:word,packs:word
  ;siz=# of entries(files)alloced for
  ;packs=# of pack files that can be opened

  pushad  ;FIX : v2.01

  .if pack_on  ;already installed?
    jmp bad
  .endif
  xor eax,eax
  mov ax,siz
  mov pts,eax
  mov ebx,sizeof ps
  mul ebx
  mov ecx,eax
  invoke malloc,eax
  cmp eax,NULL
  jnz @f
bad:
  .if pt
    callp free,pt
    mov pt,0
  .endif
  .if ph
    callp free,ph
    mov ph,0
  .endif
  popad
  mov errno,ENOMEM
  mov eax,ERROR
  ret
@@:
  mov pt,eax
  mov edi,eax
  xor al,al
  rep stosb  ;clear table
  xor eax,eax
  mov ax,packs
  mov phs,eax
  shl eax,1
  mov ecx,eax
  callp malloc,eax
  cmp eax,NULL
  jz bad
  mov ph,eax
  mov edi,eax
  xor al,al
  rep stosb  ;clear table
  mov pack_on,1

  callp getint,21h
  mov word ptr[old_int21h+4],ax
  mov dword ptr[old_int21h],edx
  mov ax,selcode
  mov edx,offset int_21h
  callp setint,21h,ax,edx
  popad
  xor eax,eax  ;FIX : v2.01
  ret
pack_init endp

;it is not necessary to call this during exit()
pack_uninit proc uses esi ecx
  .if !pack_on
    mov eax,ERROR
    ret
  .endif

;restore oringinal DOS INT 21h handler
  mov ax,word ptr[old_int21h+4]
  mov edx,dword ptr[old_int21h]
  callp setint,21h,ax,edx

;close all files used
  mov esi,ph
  mov ecx,phs
@@:
  cmp wptr[esi],0
  .if !zero?
    callp close,wptr[edi]
  .endif
  add esi,2
  dec ecx
  jnz @b

;free buffers
  callp free,pt
  mov pt,0
  callp free,ph
  mov ph,0

  mov pack_on,0
  xor eax,eax
  ret
pack_uninit endp

open_pack proc private,hdr:dword,h:word
  mov esi,hdr
  mov ebx,noff
  mov edi,pt
  mov edx,pts
  mov ax,h
@@:
  cmp bptr[edi],0
  jz add1file
  add edi,sizeof ps
  dec edx
  jnz @b
  mov eax,ERROR   ;not enough room to load all files
  ret
add1file:
  mov [esi].ps.fh,ax
  mov ecx,sizeof ps
  rep movsb
  dec edx
  dec ebx
  jnz @b
  xor eax,eax ;all loaded it
  ret
open_pack endp

pack_open proc,strg:dword  ;opens a packed file
  local h:word,buf:dword

  pushad  ;FIX : v2.01
;look for an empty location in ph
  mov ecx,phs
  xor eax,eax
  mov edi,ph
  repnz scasw
  .if !zero?
    ;non-found
    jmp bad
  .endif
  ;found one!
  sub edi,2  ;edi=>open location
  callp openpack,strg,0  ;for reading only! (objections?)
  cmp eax,ERROR
  jnz @f
bad:
  popad
  mov eax,ERROR
  ret
@@:
  stosw   ;save handle
  mov h,ax
;now to read in the files  (can't use READ cause it will screw it up)
  callp readpack,ax,offset temp4,6
  .if eax!=6
    callp closepack,h
    jmp bad
  .endif
  mov ebx,offset temp4
  cmp dword ptr[ebx],01a4b4150h ;'PAK',26
  .if !zero?
    callp closepack,h
    jmp bad
  .endif
  xor eax,eax
  mov eax,noff
  mov ebx,sizeof ps
  mul ebx 
  mov ecx,eax  ;siz (save for l8r)
  callp malloc,eax  ;a temp buffer needed to quickly load all structs
  .if eax==ERROR
    callp closepack,h
    jmp bad
  .endif
  mov buf,eax
  callp readpack,h,buf,ecx

  callp open_pack,buf,h
  mov ebx,eax  ;save eax...
  callp free,buf  ;release temp buffer
  .if ebx      ;...for here
    callp closepack,h
    popad
    mov eax,ERROR
    ret
  .endif
  popad
  xor eax,eax
  mov ax,h
  ret
pack_open endp

;ADDED : v2.01
  ;opens a packed file (with header already loaded)
  ;these pack files must have been created with the -h option with PACKIT.EXE
pack_open_hdr proc,strg:dword,hdr:dword
  local h:word

  pushad

;look for an empty location in ph
  mov ecx,phs
  xor eax,eax
  mov edi,ph
  repnz scasw
  .if !zero?
    ;non-found
    jmp bad
  .endif
  ;found one!
  sub edi,2  ;edi=>open location
  callp openpack,strg,0  ;for reading only
  cmp eax,ERROR
  jnz @f
bad:
  popad
  mov eax,ERROR
  ret
@@:
  stosw   ;save handle
  mov h,ax
;now to read in the files
  callp readpack,ax,offset temp4,4
  .if eax!=4
    callp closepack,h
    jmp bad
  .endif
  mov ebx,offset temp4
  cmp dword ptr[ebx],01a484150h ;'PAH',26  ;note: it's PAH for Pack files without headers
  .if !zero?
    callp closepack,h
    jmp bad
  .endif
  xor eax,eax
  mov ebx,hdr
  mov ax,[ebx]
  mov wptr[noff],ax
  mov eax,hdr
  add eax,2  ;skip # of files
  callp open_pack,eax,h
  .if eax
    popad
    mov eax,ERROR
    ret
  .endif
  popad
  xor eax,eax
  mov ax,h
  ret
pack_open_hdr endp

align 4
pack_close proc,h:word
  pushad
  mov ax,h
  mov esi,pt
  mov ecx,pts
@@:
  cmp [esi].ps.fh,ax
  .if zero?
    mov bptr[esi],0  ;erase filename (empty it)
  .endif
  add esi,sizeof ps
  dec ecx
  jnz @b
  callp closepack,ax
  mov edi,ph
  mov ecx,phs
  mov ax,h
  repnz scasw
  .if zero?
    mov wptr[edi-2],0
    popad
    xor eax,eax
    ret
  .endif
  popad
  mov eax,ERROR
  ret
pack_close endp

align 4
int_21h:
  push ds
  mov ds,cs:seldata
  cmp pack_on,0
  jz do_dos
  cmp ah,3dh
  jz _open
  cmp ah,3eh
  jz _close
  cmp ah,3fh
  jz _read
  cmp ah,42h
  jz _lseek
do_dos:
  pop ds
  jmp cs:[old_int21h]

_open:
;search in tables
  cmp chand,0
  jnz do_dos  ;other handle already open!
  pushad
  push es
  mov es,cs:seldata
  mov esi,edx ;strg
  mov edi,offset fn
  xor ecx,ecx
  cld
@@:           ;capatalize the string
  lodsb
  cmp al,0
  jz @f
  cmp al,'\'
  jz notfound
  cmp al,':'
  jz notfound
  .if ((al>='a')&&(al<='z'))
    sub al,32
  .endif
  stosb
  inc ecx
  .if ecx>12
    jmp notfound
  .endif
  jmp @b
@@:
  .if bptr[edi-1]=='.'
    mov bptr[edi-1],0
  .else
    xor al,al
    stosb
  .endif
  mov ebx,pt
  mov ecx,pts
  mov eax,sizeof ps
  mul ecx
  mov edx,offset fn
  add ebx,eax
  sub ebx,sizeof ps  ;ptrs to last entry (so last occurance of file be used instead)
@@:
  cmp bptr[ebx],0
  .if !zero?
    callp strcmp,edx,ebx
    cmp al,0
    jz found
  .endif
  sub ebx,sizeof ps      ;search thru list backwards!!!
  dec ecx
  jnz @b
notfound:
  pop es
  popad
  jmp do_dos
found:
;pack file found!
  mov ax,[ebx].ps.fh   ;handle
  mov edx,[ebx].ps.off ;offset
  mov ecx,[ebx].ps.siz ;size
  mov coff,edx
  mov cend,edx
  mov chand,ax
  mov cpos,0
  mov csiz,ecx
  add cend,ecx
  mov ecx,edx
  shr ecx,16  ;CX:DX
  mov bx,ax
  mov ax,4200h
  ; callp lseek,ax,ecx,SEEK_SET
  ;can't use this here, LSEEK will override and cause relative seek
  pushfd
  call [old_int21h]
  .if carry?
    ;error
    pop es
    popad
    pop ds
    or byte ptr[esp+8],1      ;set carry on error
    ; flags , cs , eip
    iretd
  .endif
  ;successful
  pop es
  popad
  pop ds
  and byte ptr[esp+8],0feh  ;clear carry
  ; flags , cs , eip
  mov ax,cs:chand
  iretd

_close:
  ;bx=hand
  pushad
  mov ax,bx
  mov esi,ph   ;handlers in use
  mov ecx,phs
@@:
  cmp [esi],ax
  jz @f
  add esi,2
  dec ecx
  jnz @b
  popad
  jmp do_dos
@@:
  popad
  mov chand,0      ;Forget about this thing  áeta V0.03
  pop ds
  and byte ptr[esp+8],0feh  ;clear carry
  ; ret,cs,flags
  iretd

_read:
  ;bx=hand
  pushad
  mov ax,bx
  mov esi,ph   ;handlers in use
  mov edx,phs
@@:
  cmp [esi],ax
  jz @f
  add esi,2
  dec edx
  jnz @b
  popad
  jmp do_dos
@@:
  ;reading with a pack file!!
  ;must make sure we don't read past EOF
  ;ecx=MAX TO READ
  mov eax,ecx
  add eax,cpos
  .if eax>csiz
    mov ecx,csiz
    sub ecx,cpos  ;read what is left over  (=0 if cpos=csiz)
  .endif
  mov [esp+6*4],ecx  ;save into ecx
  popad
  pushfd
  call [old_int21h]
  ;gotta fix cpos to ptr to proper location within file
  .if carry?
    or byte ptr[esp+12],1      ;set carry
  .else
    and byte ptr[esp+12],0feh  ;clear carry
    add cpos,eax
  .endif
  pop ds
  iretd

_lseek:
  ;bx=hand
  pushad
  mov ax,bx
  mov esi,ph   ;handlers in use
  mov ecx,phs
@@:
  cmp [esi],ax
  jz @f
  add esi,2
  dec ecx
  jnz @b
  popad
  jmp do_dos
@@:
  popad
  ;LSEEKing with a pack file!!
  ;convert CX:DX => EDX
  shl ecx,16
  mov cx,dx
  mov edx,ecx
  .if al==0
    add edx,coff
  .endif
  .if al==1    ;FIX : v2.01 : greatly updated
    .if edx & 80000000h  ;neg?
      add edx,cpos
      add edx,coff
      .if edx > cend || edx < coff
        mov edx,coff
      .endif
    .else
      add edx,cpos
      add edx,coff
      .if edx > cend || edx < coff
        mov edx,cend
      .endif
    .endif
    xor al,al
  .endif
  .if al==2  ;this is not allowed if edx>0 so I just ignore edx!!
    add edx,cend
    .if edx>cend || edx < coff
      mov edx,cend
    .endif
    xor al,al
  .endif
  .if al>2  ;???
    jmp badseek
  .endif
  mov ecx,edx
  shr ecx,16   ;CX:DX
  pushfd
  call [old_int21h]   ;DX:AX
  ;gotta fix eax to ptr to proper location within file
  .if carry?
    ;ds,ret,cs,flg
badseek:
    or byte ptr[esp+12],1      ;set carry
  .else
    ; DX:AX -> EAX
    shl edx,16
    mov dx,ax
    mov eax,edx
    and byte ptr[esp+12],0feh  ;clear carry
    sub eax,coff
    mov cpos,eax   ;new pos
    ; EAX -> DX:AX
    mov edx,eax
    shr edx,16
  .endif
  pop ds
  iretd

;in:
;ah=45h
;bx=handle
;out:
;ax=new handle
; NOTE: changing offset of one changes the other.

[ RETURN TO DIRECTORY ]