;savecd.asm heha/stern, Aug. 1997
; redirect savegame writes for CD games to harddisk dir
JUMPS
SMART
LOCALS
MODEL TINY
MAGICFUNC EQU 1842h ;unused function for CP/M compat.
MAGICCODE EQU 123Ah
MAGICANS EQU 7721h
DOS MACRO num
IFNB <num>
IF num GT 255
mov ax,num
ELSE
mov ah,num
ENDIF
ENDIF
int 21h
ENDM
SES MACRO mem:req,reg:rest
mov word ptr mem[0],reg
mov word ptr mem[2],es
ENDM
LD MACRO dest:req,src:rest
push src
pop dest
ENDM
NL EQU <13,10>
CODESEG
ComBase:
org 2Ch
EnvSeg dw ?
org 72h
hFileDst dw 0
FF_CodeAX dw ?
FF_AttrCX dw ?
data_0209 dw ?
currfhandle dw ?
FF_Flag db ?
EnterLock db ?
UnlockForExec db ?
EndOfPath dw ?
ThePath db 80h dup (?)
org 100h
start: jmp start_here
DriveRestrict db 0
fhandletab db 256 dup (0)
transferbuf db 1000h dup (?) ;4K fr alle Flle
tbend:
tbfill dw ?
BufferForFF db 128 dup (?)
;========================================================================
NewInt21 proc far
cmp ax,MAGICFUNC
jne @@fwd
cmp bx,MAGICCODE
jne @@fwd
mov ax,MAGICANS
mov bx,cs
iret
@@fwd:
cmp ah,4Bh ; "exec"
jne @@fwd2
cmp cs:UnlockForExec,1
jne @@fwd2
mov cs:UnlockForExec,0
mov cs:enterlock,1 ; unlock
jmp loc_13FA ; and continue oldint (= run game)
@@fwd2:
cmp cs:enterlock,0 ; locked?
je loc_13FA ; yes - continue oldint
mov cs:enterlock,0 ; lock!
cmp ah,39h ; "mkdir"
je try_mirror_only
cmp ah,3Ah ; "rmdir"
je try_mirror_only
cmp ah,41h ; "unlink"
je try_mirror_only
cmp ah,3Ch ; "creat"
je try_mirror_only
cmp ah,3Dh ; "open"
je open
cmp ah,43h ; "chmod"
je try_mirror_then_cd
cmp ah,4Bh ; "exec"
je exec_mirror_then_cd
cmp ah,4Eh ; "findfirst"
je findfirst
cmp ah,4Fh ; "findnext"
je findnext
cmp ah,36h ; "df"
jne int21cont
diskfree:
pushf ; Push flags
cmp cs:[DriveRestrict],0
jz @@notest ;mangle always
cmp cs:[DriveRestrict],dl ; this request
jnz @@nomangle
@@notest:
mov dl,cs:ThePath ; default 'C'
sub dl,'@' ; dos drv counted from 1
@@nomangle:
popf ; Pop flags
int21cont:
mov cs:enterlock,1 ; release
loc_13FA:
db 0EAh ;JMP FAR opcode
OldInt21 dd ?
int21exi:
mov cs:enterlock,1
retf 2 ; Return far
NewInt21 endp
;====== handle dos functions =====================================
try_mirror_only:
sti ; Enable interrupts
call manglepath
jc int21cont
push ds dx
LD ds,cs
lea dx,ThePath
DOS
pop dx ds
jmp int21exi
open:
sti ; Enable interrupts
call manglepath
jc int21cont
test al,7 ; access code
jnz openw ; if w+
push ds dx
LD ds,cs
lea dx,ThePath
push ax
DOS
jnc no_openerr
pop ax
pop dx ds
DOS
jmp int21exi
no_openerr:
pop dx
pop dx ds
jmp int21exi
openw:
test al,6 ; rw
;For read/write access, it's necessary to copy the desired file onto
;config file directory before allowing access to it!
jnz loc_1450
jmp loc_1450
; never reached
push ds dx
LD ds,cs
lea dx,ThePath
DOS
pop dx ds
jmp int21exi
loc_1450:
push ds dx
LD ds,cs
lea dx,ThePath ;First check whether local copy exist
push ax cx
DOS 4300h ; get file attrb, nam@ds:dx
pop cx ax
jc @@NotExist
DOS ;If exist, open normally
pop dx ds
jmp int21exi
@@NotExist:
pop dx ds
push ax
DOS 3D00h ; open CD file for read
jnc loc_1476 ;success
inc sp ;Open even for read fails, report errors
inc sp
jmp int21exi
loc_1476:
mov cs:currfhandle,ax ;read file handle
push cx bx si
lea bx,fhandletab
seeknext:
push ax
mov al,cs:[bx]
or al,al ; free?
pop ax
jz free_slot_found
push bx
mov si,cs:data_0209 ; ??
call stricmp
pop bx
jz loc_14B0
loc_1495:
push ax
mov al,cs:[bx]
or al,al ; Zero ?
pushf ; Push flags
inc bx
popf ; Pop flags
pop ax
jnz loc_1495
jmp seeknext
free_slot_found:
pop si bx cx
mov ax,cs:currfhandle ; (=0)
clc ; Clear carry flag
inc sp ;skip pushed AX (OpenMode)
inc sp
jmp int21exi
loc_14B0:
pop si bx
push ds dx
LD ds,cs
lea dx,ThePath
xor cx,cx ; Zero register
DOS 3Ch ; create/truncate file @ ds:dx
jnc loc_14C9
pop dx ds cx
inc sp ;skip pushed CX
inc sp
jmp int21exi
loc_14C9:
mov hFileDst,ax ; (=0)
@@FileCopy:
mov bx,currfhandle
mov cx,1000h ;sizeof TransferBuf
lea dx,TransferBuf
DOS 3Fh ; read file, cx=bytes, to ds:dx
jc @@CopyEnd
or ax,ax ;EOF reached?
jz @@CopyEnd ;ready!
mov cx,ax
mov bx,hFileDst ; (=0)
DOS 40h ; write file cx=bytes, to ds:dx
jc @@CopyEnd
cmp cx,ax
je @@FileCopy
@@CopyEnd:
mov bx,currfhandle ; (=0)
DOS 3Eh ; close file, bx=file handle
mov bx,hFileDst
DOS 3Eh ; close file, bx=file handle
lea dx,ThePath
DOS 3D02h ; open file, al=mode,name@ds:dx
pop dx ds cx
inc sp
inc sp
jmp int21exi
try_mirror_then_cd:
sti ; Enable interrupts
call manglepath
jc int21cont
push es bx ds dx cx ax
LD ds,cs
lea dx,ThePath
DOS ; try local
jnc @@local_done
pop ax cx dx ds bx es
DOS ; retry on CD
jmp int21exi
@@local_done:
pop dx dx dx ds bx es
jmp int21exi
exec_mirror_then_cd:
sti ; Enable interrupts
call manglepath
jc int21cont ; no business on requested drive
push es bx ds dx cx ax
mov cs:[UnlockForExec],1
LD ds,cs
lea dx,ThePath
DOS ; try local
jnc @@exec_done
mov cs:[UnlockForExec],1
pop ax cx dx ds bx es
DOS ; retry CD
jmp int21exi
@@exec_done:
pop dx dx dx ds bx es
jmp int21exi
findfirst:
mov cs:FF_Flag,1
mov cs:FF_CodeAX,ax
mov cs:FF_AttrCX,cx
pushf ; Push flags
push es si di
cld ; Clear direction
mov si,dx
lea di,BufferForFF
LD es,cs
@@CopyStr:lodsb
stosb
or al,al ; Zero ?
jnz @@CopyStr
pop di si es
popf ; Pop flags
mov ax,cs:FF_CodeAX ; (=0)
call manglepath
mov cs:FF_Flag,0
mov cs:[tbfill],offset transferbuf ;reset list
jc int21cont
push ds dx
LD ds,cs
lea dx,ThePath
DOS ;First scan through the cfg dir
pop dx ds
hoho:
jc OnFnError
jmp haha
OnFnError:
pushf ; Push flags
cmp cs:FF_Flag,0 ;Is this the second occurence of CY?
je @@FindInCdDir ;no, find direntries in CD dir
popf ;yes, report error
jmp int21exi
@@FindInCdDir:
popf ; Pop flags
mov cs:FF_Flag,1 ;Inhibit recursion
push ds cx dx
LD ds,cs
lea dx,BufferForFF
mov cx,cs:FF_AttrCX
mov ax,cs:FF_CodeAX
DOS ;Make FindFirst for CD
pop dx cx ds
jc int21exi
haha:
call CheckDoublette
jnc int21exi ;okay if allowed to display
push ax ;preserve AX
DOS 4Fh
jc @@huhu
pop ax ;restore AX
jmp haha
@@huhu: inc sp
inc sp
jmp OnFnError ;leave error code in AX
findnext:
DOS
jmp hoho
CheckDoublette proc
;PA: CY=1 if filename match detected, OR directory in cfg dir
pushf
push ax bx cx ds si es di
DOS 2Fh ;get DTA in ES:BX
LD ds,es
LD es,cs
lea si,[bx+30] ;adress of ASCIIZ name portion
cld
;now: DS:SI=filename, ES:transferbuf=buffer
cmp cs:[FF_Flag],0 ;Cfg dir?
jnz @@CheckPresence
;check if directory other than '.' and '..'
test byte ptr [bx+21],10h ;test directory bit
jz @@copy ;okay, it's a file
cmp byte ptr [si],'.' ;test . or .. directory
jne @@excy ;nope, report error
;copy filename portion into buffer
@@copy:
mov di,es:[tbfill]
mov cx,6
rep movsw ;copy 12 bytes
cmp di,offset tbend-11
jnc @@OvlPrevent ;prevent buffer overrun
mov es:[tbfill],di ;new end address
@@OvlPrevent:
jmp @@exi
@@CheckPresence:
lea di,transferbuf
mov cx,12
jmp @@foot
@@rep:
push si di cx
@@l: lodsb ;AL:=DS:[SI]
scasb ;ES:[DI]-AL
jnz @@nomatch
or al,al
loopnz @@l
jmp @@match
@@nomatch:
pop cx di si
add di,cx
@@foot:
cmp di,es:[tbfill]
jc @@rep
@@exi:
pop di es si ds cx bx ax
popf
clc
ret
@@match:
add sp,6
@@excy:
pop di es si ds cx bx ax
popf
stc
ret
CheckDoublette endp
;--------------------------------------------------------------------------
manglepath proc near
;appends the basename of the path given in DS:DX to the mirror path
;returns CY=1 if the specified or current drive doesn't match
push si ax
pushf
cmp cs:[DriveRestrict],0
jz @@notest ;mangle always
mov si,dx
cmp byte ptr [si+1],':'
jnz @@GetCurDrive
mov al,[si] ;Laufwerksbuchstabe
call upcase
sub al,'@'
jmp @@testdrv
@@GetCurDrive:
DOS 19h
inc al
@@testdrv:
cmp cs:[DriveRestrict],al
jnz @@exi ;exiting with carry set
@@notest:
push dx es di
LD es,cs
cld
mov si,dx
;scan for last ':/\' in string
@@mem: mov dx,si ;memorize possible string begin
@@scn: lodsb
cmp al,':'
je @@mem
cmp al,'/'
je @@mem
cmp al,'\'
je @@mem
or al,al
jnz @@scn
mov si,dx
mov di,es:[EndOfPath]
@@cat: lodsb ;append filename
stosb
or al,al
jnz @@cat
pop di es dx
popf
clc ;never fail here
pop ax si
ret
@@exi:
popf
stc
pop ax si
ret
manglepath endp
upcase proc
cmp al,'a'
jb @@e
cmp al,'z'
ja @@e
and al,not 20h
@@e: ret
upcase endp
;----------------------------------------------------------------------------
; very buggy stricmp(char *si, char *bx) (exits on ne or e)
; returns z=1 if strings equal or *si ends
; I: si = pt
stricmp proc near
push ax
cld
@@l: segcs lodsb
call upcase
xchg ah,al
mov al,cs:[bx]
inc bx
call upcase
or ax,ax ;both strings terminate?
jz @@e
cmp al,ah ;both chars are equal?
jz @@l
@@e:
pop ax
ret
stricmp endp
ResEnd:
PreparePath proc
;I: DS:SI - the desired new directory name (terminated roughly by white space)
; ES:80h - internal storage area for canonicalised dirname (=command line)
;O: CY=1: error, AL=DOS error code 2:File not found, 3:Path not found
; 255: Exists as file, 5: permission denied, 0: User abort
;C: CX
;
;Create dir if it doesn't exist (ask user).
;Performs recursive MKDIR if necessary (mkdir -p).
;
lea di,ThePath ;where the directory goes to (ES:DI)
; make ASCIZ string
@@l: lodsb ;seek for end of string
stosb
cmp al,21h
jnc @@l
dec di ;onto terminating char
xor al,al
stosb ;terminate string for TrueName
; call truename() - varying behaviour for terminating backslashes!
push ds
LD ds,es
lea si,ThePath
mov di,si
DOS 60h ;TRUENAME; in-place
pop ds
jc @@e ;FATAL
; check if name exists as dir or file (use getattrib)
push ds
LD ds,es
lea dx,ThePath
DOS 4300h ;GetFAttr
pop ds
jc @@AskUser ;failed
; is a directory? (stern)
test cl,10h
stc
mov al,255
jz @@e ;fatal
@@AppendSlash:
lea di,ThePath
xor al,al
mov cx,0FFFFh ;unlimited search
repne scasb ;place DI _after_ the zero byte
dec di
mov al,es:[di-1] ;last char of string
cmp al,'\' ;check for trailing '/\:'
jz @@termok
cmp al,'/' ;should never occur
jz @@termok
cmp al,':' ;should never occur
je @@termok
mov al,'\'
stosb ;append missing '\'
@@termok:
mov es:[EndOfPath],di ;memorize end of string
clc
@@e: ret
@@AskUser:
lea dx,Ask$ ;output a newline
DOS 9
@@loop:
DOS 1 ;GetChar
and al,not 20h ;upcase
cmp al,'Y'
jz @@permit
cmp al,'J'
jz @@permit
cmp al,'Z'
jz @@permit
cmp al,'N' ;return zero if aborting (!!)
jnz @@loop
lea dx,nl$
DOS 9
xor al,al
stc
ret
@@permit:
lea dx,nl$
DOS 9
xor cx,cx
@@retry:
push ds
LD ds,es
lea dx,ThePath
DOS 39h
pop ds
jnc @@fwd ;no error, creation OK
cmp al,3
stc
jnz @@e ;error other than 3 - FATAL
push cx
lea di,ThePath
xor al,al
mov cx,0FFFFh
cld
repnz scasb
dec di ;onto zero!
not cx ;CX=strlen()+1
mov al,'\'
std
repne scasb ;strrchr(...,'\')
cld
pop cx
stc
mov al,5 ;evil error
jne @@e ;if backslash not found (wrt scasb)
mov byte ptr es:[di+1],0 ;split string at '\'
inc cx ;count repeats
jmp @@Retry
@@fwd: jcxz @@AppendSlash
@@loo: lea di,ThePath
push cx
xor al,al
mov cx,0FFFFh
repnz scasb
dec di
pop cx
mov byte ptr es:[di],'\'
push ds
LD ds,es
lea dx,ThePath
DOS 39h
pop ds
jc @@e
loop @@loo
jmp @@AppendSlash
PreparePath endp
start_here proc
lea dx,Copyr$
DOS 9 ; display char string at ds:dx
DOS 30h ; get DOS version number ax
cmp al,3
jae @@DosVer_OK
mov dx,offset EDosVer$
DOS 9 ; display char string at ds:dx
DOS 4CFEh ; exit(-2)
@@DosVer_OK:
DOS 3521h ; get intrpt vector 21h in es:bx
SES OldInt21,bx
push ds
pop es
mov ch,0 ;marker register
mov bx,MAGICCODE
DOS MAGICFUNC
cmp ax,MAGICANS
jne @@NotRes
mov es,bx ;move segment of resident copy to ES
or ch,80h ;mark: resident copy present
cmp word ptr OldInt21[0],offset NewInt21
jne @@NotRes
cmp word ptr OldInt21[2],bx
jne @@NotRes
or ch,40h ;mark: okay for uninstall
@@notres:
cld ; Clear direction
mov si,81h ;Now parse command line
@@NextChar:
lodsb ; String [si] to al
cmp al,' '
je @@NextChar ;skip whitespace
cmp al,9
je @@NextChar
cmp al,13
je @@CmdEnd
cmp al,'/'
je @@opt
cmp al,'-'
je @@opt
dec si ;Something else? - directory name!
push cx
call PreparePath
pop cx
jnc @@CopyEnd
sub al,2
lea dx,EFile
jz PrintExit
lea dx,EPath
dec al ;3
jz PrintExit
sub al,5-3 ;5
lea dx,EAccess
jz PrintExit
sub al,255-5
lea dx,EExistAsFile
jz PrintExit
DOS 4CFFh ;user abort with setting errorlevel
@@opt:
lodsb
cmp al,'?'
je @@Usage
or al,20h ;lowercase
cmp al,'h'
je @@Usage
cmp al,'u'
je DoUninst
cmp al,'d'
je @@SetDriveRestrict
@@SN_Err:
lea dx,ESyntax$
jmp PrintExit
@@Usage:
lea dx,Usage$
jmp PrintExit
@@SetDriveRestrict:
lodsb
cmp al,'='
jz @@skipdelim
cmp al,':'
jnz @@nodelim
@@skipdelim:
lodsb
@@nodelim:
sub al,'+'
je @@PokeAL ;poke zero to enable all drives
add al,'+'
cmp al,'-' ;have "-" as "no drive"
je @@PokeAL
cmp al,21h
jc @@UseCurr
call upcase
sub al,'A'
cmp al,26 ;must be 0..25
jnc @@SN_Err
jmp @@UseDriveAL
@@UseCurr:
dec si
DOS 19h
@@UseDriveAL:
inc al ;!!!
@@PokeAL:
mov es:[DriveRestrict],al
lodsb
cmp al,':'
jnz @@NoSkipColon
lodsb
@@NoSkipColon:
cmp al,21h
jnc @@SN_Err
dec si
jmp @@NextChar
@@CmdEnd:
test ch,80h ;Resident copy available?
jz @@SN_Err
lea dx,AlrRes$
DOS 9
mov al,es:[DriveRestrict]
lea dx,All$
or al,al
jz @@printdrv
lea dx,None$
cmp al,26
ja @@printdrv
add al,'A'-1
mov dl,al
mov ah,2 ;use fcn 2 instead of 9
@@printdrv:
DOS
lea dx,AlrRes2$
DOS 9
push ds
LD ds,es
lea dx,ThePath
mov cx,[EndOfPath]
sub cx,dx ;CX=string length
mov bx,1 ;stdout
DOS 40h
pop ds
lea dx,AlrRes3$
PrintExit:
DOS 9 ; display char string at ds:dx
lea dx,nl$
DOS
DOS 4C00h ; terminate with al=return code
DoUninst:
test ch,80h
lea dx,NoRes$
jz PrintExit
test ch,40h
lea dx,CantRemov$
jz PrintExit
push ds
lds dx,es:[OldInt21]
DOS 2521h ;unhook Int21 chain
pop ds
DOS 49h ;free resident copy
lea dx,UnInst$
jmp PrintExit
@@CopyEnd:
test ch,80h
lea dx,DirChange$
jnz PrintExit
mov enterlock,1 ;enable ISR
mov UnlockForExec,0 ;clear exec handling flag
lea dx,NewInt21
DOS 2521h ; set intrpt vector al to ds:dx
lea dx,Install$
DOS 9 ; display char string at ds:dx
mov es,EnvSeg
DOS 49h ; release memory block, es=seg
mov bx,4 ;stdaux
@@cl: DOS 3eh ;close file handle
sub bx,1 ;all other stdXXX
jnc @@cl
mov dx,((offset ResEnd-offset ComBase)+15) shr 4
DOS 3100h ; terminate & stay resident
start_here endp
nl$ db nl,'$'
Copyr$ db 'SaveCD V2.3 (h#s,dino,08/97): $'
AlrRes$ db 'bereits resident, wirksam auf Laufwerk $'
AlrRes2$ db ',',nl,'Spiegelverzeichnis="$'
AlrRes3$ db '"$'
None$ db '[kein]$'
All$ db '[alle]$'
DirChange$ db 'Verzeichnis aktualisiert.$'
UnInst$ db 'erfolgreich entfernt.$'
NoRes$ db 'ist nicht resident!$'
CantRemov$ db 'kann nicht deinstalliert werden!$'
Install$ db 'resident installiert.',nl,'$'
EDosVer$ db 'bentigt mindestens DOS 3.0!$'
ESyntax$ db 'Optionen: [/d[:Laufwerk]] [/u] [Verzeichnis]$'
Usage$ db 'Optionen: [/d[:Laufwerk]] [/u] [Verzeichnis]',nl
db 'Blended Inhalt von "Verzeichnis" auf "Laufwerk" ein. '
db 'Laufwerk ist: Buchstabe/"+" fr alle (default)/"-" fr keines.'
db 'Ermglicht das Speichern von Spielstnden.$'
Ask$ db 'Verzeichnis existiert nicht - Anlegen [J/N]? $'
EPath db 'Pfad nicht gefunden!$'
EFile db 'Datei nicht gefunden!$'
EAccess db 'Zugriff verweigert!$'
EExistAsFile db 'angegebener Verzeichnisname existiert als Datei!$'
END start
Detected encoding: UTF-8 | 0
|