;-------------------------------------------------------------------------------
;  colibrary procedure SetSubKey
;
; -------------------------------------------------------
; This procedure was written by Ernest Murphy    9/27/00
;
;  revised 2/1/01 for register value bug (2 omited lines 
;           added after ".IF (Flags & StringFlag)"
;
; Copyright (c) 9/28/00  Ernest Murphy
; For educational use only. Any commercial re-use only by written license
;
; -------------------------------------------------------
.NOLIST
.386
.model flat, stdcall  ; 32 bit memory model
option casemap :none  ; case sensitive

include     mini_win.inc	; 'just enough' of windows.inc (speeds build)
include     \masm32\include\oleaut32.inc
include     \masm32\include\kernel32.inc
include     \masm32\include\user32.inc
include     \masm32\include\advapi32.inc
include     \masm32\include\masm32.inc
include     \masm32\COM\include\oaidl.inc

include     colib.inc

externdef   g_hHeap:DWORD
externdef   g_hModule:DWORD


.code
;-------------------------------------------------------------------------------

SetSubKey PROC OpenKey:DWORD, pszLastToken:DWORD, Install:DWORD
             
;-------------------------------------------------------------------------------
; internal lib function, used to process and save the registrar script elemsnts
;   
;
; EXAMPLE:
;   invoke GetNextToken
;
; Uses: eax, ecx, edx
;
;
;-------------------------------------------------------------------------------
    LOCAL pszKeyValueName:DWORD, pszToken:DWORD
    LOCAL szBuffer[50]:BYTE,     NewKey:DWORD
    LOCAL Flags:DWORD,           Disp:DWORD
    LOCAL StrLength:DWORD,       TempKey:DWORD
    LOCAL KeyNuked:DWORD,        SkipCount:DWORD

ExpectValFlag       EQU       1H
NamedValueFlag      EQU       2H
StringFlag          EQU       4H
DwordFlag           EQU       8H
ForceRemove         EQU      10H
NoRemove            EQU      20H
NotKeyValue         EQU      40H
ValueName           EQU     100H
KeyName             EQU     200H
TempNewKey          EQU     400H

    xor eax, eax
    mov Flags, eax              ;  clear some flags/pointers
    mov NewKey, eax
    mov pszKeyValueName, eax
    mov pszToken, eax
    mov SkipCount, eax
    mov KeyNuked, eax
GetNextT:
    .IF pszToken
        invoke HeapFree, g_hHeap, NULL, pszToken
        mov pszToken, NULL
    .ENDIF
    invoke GetNextToken
    .IF eax
        mov pszToken, eax
        ; check for "val"
        mov DWORD PTR szBuffer, "lav"
        invoke lstrcmpi, ADDR szBuffer, pszToken
        .IF !eax
            or Flags, NamedValueFlag
            jmp GetNextT
        .ENDIF

        ; check if we got a "FORCE REMOVE" string
        invoke LoadString, g_hModule, IDS_FORCE_REMOVE, ADDR szBuffer, MAX_PATH
        invoke lstrcmpi, ADDR szBuffer, pszToken
        .IF !eax
            or Flags, ForceRemove
            jmp GetNextT
        .ENDIF

        ; check if we got a "NO REMOVE" string
        invoke LoadString, g_hModule, IDS_NO_REMOVE, ADDR szBuffer, MAX_PATH
        invoke lstrcmpi, ADDR szBuffer, pszToken
        .IF !eax
            or Flags, NoRemove
            jmp GetNextT
        .ENDIF

        ; check if we got a "=" string
        mov ecx, pszToken
        mov ax, WORD PTR [ecx]
        .IF (ax == 003DH)
            ; equals sign, expect a key value
            or Flags, ExpectValFlag
            jmp GetNextT
        .ENDIF

        ; check if we got a "d" string
        mov ecx, pszToken
        mov ax, WORD PTR [ecx]
        .IF (ax == 0044H) || (ax == 0064H)
            ; dword value follows
            or Flags, DwordFlag
            jmp GetNextT
        .ENDIF

        ; check if we got a "s" string
        mov ecx, pszToken
        mov ax, WORD PTR [ecx]
        .IF (ax == 0053H) || (ax == 0073H)
            ; string value follows
            or Flags, StringFlag
            jmp GetNextT
        .ENDIF

        ; check for '{' to see if we recurse down
        mov DWORD PTR szBuffer, "{"   
        invoke lstrcmp, ADDR szBuffer, pszToken
        .IF !eax
            or Flags, NotKeyValue    ; after we process any pending info we'll recurse
        .ENDIF

        ; check for "}"
        mov ecx, pszToken
        mov ax, WORD PTR [ecx]
        .IF (ax == 007DH)
            .IF KeyNuked
                ; we did't recurse on this keys
                dec SkipCount
                mov Flags, NULL
                .IF !SkipCount
                    mov SkipCount, FALSE
                .ENDIF
                jmp GetNextT
            .ENDIF    
            ;we're all done here
            jmp return                        
        .ENDIF

        ; if we got here, we have a symbol to process.

        invoke AddReplacements, pszToken
        .IF eax
            ; we found a replacement, release orgional string
            ; and point to new string
            mov Disp, eax
            invoke HeapFree, g_hHeap, NULL, pszToken
            mov eax, Disp
            mov pszToken, eax            
        .ENDIF
; install -------------------------------------------
        .If Install
            ; installing settings
            and Flags, not NoRemove     ; reset this flag (only need for uninstall)
            .IF (Flags & ForceRemove)
                invoke GuardedDeleteKey, OpenKey, pszToken
                and Flags, not ForceRemove      ; reset flag
            .ENDIF

            .IF !Flags
                .IF !NewKey
                    invoke RegCloseKey, ADDR NewKey
                    mov NewKey, NULL
                .ENDIF
                invoke RegCreateKeyEx, OpenKey, pszToken, NULL, NULL, 
                                       REG_OPTION_NON_VOLATILE, 
                                       KEY_READ or KEY_WRITE, 
                                       NULL,ADDR NewKey, ADDR Disp
                jmp GetNextT
            .ENDIF

            .IF Flags == NamedValueFlag     ; && !(Flags & ExpectValFlag)
                ; persist token for named value use
                mov eax, pszToken
                mov pszKeyValueName, eax
                mov pszToken, NULL
                jmp GetNextT
            .ENDIF

            .IF (Flags & ExpectValFlag)
                .IF (Flags & NamedValueFlag)
                    mov eax, OpenKey
                    mov TempKey, eax
                .ELSE
                    mov eax, NewKey
                    mov TempKey, eax
                .ENDIF

                .IF (Flags & StringFlag)
                    invoke lstrlen, pszToken
                    inc eax     ; for trailing zero
                    mov StrLength, eax
                    invoke RegSetValueEx, TempKey, pszKeyValueName, 
                           NULL, REG_SZ, pszToken, StrLength 
                .ELSE ; DWORD
                    invoke atodw, pszToken
                    invoke RegSetValueEx, TempKey, pszKeyValueName, NULL, 
                           REG_DWORD_LITTLE_ENDIAN, eax, SIZEOF DWORD
                .ENDIF    
                .IF pszKeyValueName
                    invoke HeapFree, g_hHeap, NULL, pszKeyValueName
                    mov pszKeyValueName, NULL
                .ENDIF
                .IF (Flags & TempNewKey)
                    mov NewKey, NULL
                .ENDIF
                mov Flags, NULL
                jmp GetNextT
            .ENDIF
        .ELSE
            and Flags, not ForceRemove
            .IF SkipCount
                jmp CheckDone   ; nothing to do once nuked but return on '}'
            .ENDIF
            .IF !(Flags & not NoRemove) 
                ; have a key
                .IF (Flags & NoRemove)
                    ; have a keeper key, open it
                    .IF NewKey != NULL
                        invoke RegCloseKey, NewKey
                        mov NewKey, NULL
                    .ENDIF
                    invoke RegCreateKeyEx, OpenKey, pszToken, NULL, NULL, 
                                           REG_OPTION_NON_VOLATILE, 
                                           KEY_READ or KEY_WRITE, 
                                           NULL,ADDR NewKey, ADDR Disp
                    mov Flags, NULL
                    mov KeyNuked, FALSE
                .ELSE
                    ; Nuke this key
                    invoke GuardedDeleteKey, OpenKey, pszToken
                    mov KeyNuked, TRUE
                    mov Flags, NULL   
                .ENDIF
            .ENDIF
        .ENDIF
;-------------------------------------------
CheckDone:
        .IF (Flags & NotKeyValue)
            .IF KeyNuked
                ; don't recurse on nuked keys
                inc SkipCount
                mov Flags, NULL
                jmp GetNextT
            .ENDIF    
            ; we're going to recurse on this open key
            ;we're going down a level
            .IF NewKey
                invoke SetSubKey, NewKey, NULL, Install
            .ELSE
                invoke SetSubKey, OpenKey, NULL, Install
            .ENDIF
            mov Flags, NULL
            ; now parse the next line
            jmp GetNextT
        .ENDIF
        mov Flags, NULL     
        jmp GetNextT
    .ENDIF
return:
    .IF pszToken
        invoke HeapFree, g_hHeap, NULL, pszToken
    .ENDIF
    .IF !NewKey
        invoke RegCloseKey, ADDR NewKey
    .ENDIF
    ret
SetSubKey ENDP
;-------------------------------------------------------------------------------

end
