; _______________________________________________________________________________________
;| |
;| ..::FreshLib::.. Free, open source. Licensed under "Fresh artistic license." |
;|_______________________________________________________________________________________|
;
; Description: OS independent micro configuration files library.
;
; Target OS: Any
;
; Dependencies: memory.asm; strlib.asm; arrays.asm; files.asm
;
; Notes:
;
;_________________________________________________________________________________________
module "uConfig library"
; Structure of the file of uConfig:
;
; offset | size | description
; --------+-------+-----------------------------------------------------------------------
; 0 | 4 | Signature
; 4 | 4 | $0a1a0a0d (CR, LF, EOF, 00)
; 8 | 4 | Hash of the whole file including file length of the next field
; 12 | 4 | Chunk data length (N)
; 16 | N | Data chunks
; --------+-------+-----------------------------------------------------------------------
;
; Every data chunk have following structure:
;
; offset | size | description
; --------+-------+-----------------------------------------------------------------------
; 0 | 4 | Key name 4xASCII chars.
; 4 | 4 | Length of the data in bytes (K)
; 8 | 4 | Data type. Constant of cdtXXXXX (see definitions below)
; 12 | K | Chunk data.
; --------+-------+-----------------------------------------------------------------------
;
cdtNULL = 0
cdtInteger = 1 ; 32bit integer value.
cdtString = 2 ; utf-8 string.
cdtBlob = 3 ; arbitraty sized array of bytes.
cdtConfig = 4
cdtMaxAlowed = cdtConfig
struct TConfigHeader
.signature dd ?
.filler dd ?
.hash dd ?
.length dd ?
.chunks:
ends
struct TChunkHeader
.KeyName dd ?
.length dd ? ; length of the data without the size of the header and checksum.
.type dd ?
ends
struct TConfigRecord
.KeyName dd ?
.DataSize dd ?
.Type dd ?
.Data dd ?
ends
; loads the file representation from the memory and expands it to the database tree structure.
; returns pointer to TArray of TConfigRecord structures.
proc LoadConfigDB, .ptrSource, .signature
begin
push ecx edx esi
mov esi, [.ptrSource]
test esi, esi
jnz .process_source
stdcall CreateArray, sizeof.TConfigRecord
jmp .db_ok
.process_source:
; check 8 bytes signature
mov edx, [esi+TConfigHeader.signature]
cmp [.signature], -1
je .signatureok
cmp edx, [.signature]
jne .error_bad_signature
.signatureok:
mov [.signature], edx
cmp dword [esi+TConfigHeader.filler], $001a0a0d
jne .error_bad_signature
mov ecx, [esi+TConfigHeader.length]
lea esi, [esi+TConfigHeader.length]
add ecx, 4 ;(the length itself)
stdcall DataHash, esi, ecx
cmp eax, [esi-4]
jne .error_bad_hash
add esi, 4
sub ecx, 4
stdcall __DoRecurseConfigSource, esi, ecx
.db_ok:
pushd [.signature]
popd [eax+TArray.lparam] ; store the signature to the .lparam field of the root TArray
clc
.finish:
pop esi edx ecx
return
.error_bad_signature:
mov eax, -1
stc
jmp .finish
.error_bad_hash:
mov eax, -2
stc
jmp .finish
endp
proc __DoRecurseConfigSource, .ptrSource, .length
.array dd ?
begin
pushad
stdcall CreateArray, sizeof.TConfigRecord
mov [.array], eax
mov esi, [.ptrSource]
mov ecx, [.length]
.chunk_loop:
cmp ecx, sizeof.TConfigRecord
jb .finish
stdcall AddArrayItems, [.array], 1
mov [.array], edx
mov edi, eax
mov eax, [esi+TChunkHeader.KeyName]
mov edx, [esi+TChunkHeader.length]
mov [edi+TConfigRecord.KeyName], eax
mov [edi+TConfigRecord.DataSize], edx
mov eax, [esi+TChunkHeader.type]
mov [edi+TConfigRecord.Type], eax
add esi, sizeof.TChunkHeader
sub ecx, sizeof.TChunkHeader
and eax, $7
movzx eax, [.type_handlers+eax]
add eax, .type_handlers
call eax
mov [edi+TConfigRecord.Data], eax
.next_chunk:
mov eax, [edi+TConfigRecord.DataSize]
add eax, 3
and al, $fc
add esi, eax
sub ecx, eax
jmp .chunk_loop
.finish:
popad
mov eax, [.array]
return
.type_handlers db .handle_null - .type_handlers
db .handle_int - .type_handlers
db .handle_string - .type_handlers
db .handle_blob - .type_handlers
db .handle_config - .type_handlers
db .handle_blob - .type_handlers
db .handle_blob - .type_handlers
db .handle_blob - .type_handlers
.handle_config:
stdcall __DoRecurseConfigSource, esi, edx
.handle_null:
retn
.handle_int:
mov eax, [esi]
retn
.handle_string:
stdcall StrNew
stdcall StrCopyPart, eax, esi, 0, edx
retn
.handle_blob:
stdcall GetMem, edx
push esi edi ecx
mov edi, eax
mov ecx, edx
rep movsb
pop ecx edi esi
retn
endp
; creates memory image (TArray) of the file of the config database.
proc SaveConfigFile, .ptrRoot, .signature
begin
push edx ecx
stdcall CreateArray, 4
mov edx, eax
mov ecx, [.ptrRoot]
stdcall AddArrayItems, edx, sizeof.TConfigHeader/4
pushd [.signature] $001a0a0d
popd [eax+TConfigHeader.filler] [eax+TConfigHeader.signature]
stdcall __DoSaveParamArray, edx, [.ptrRoot]
push edx
mov ecx, [edx+TArray.count]
shl ecx, 2
lea edx, [edx+TArray.array+TConfigHeader.length]
sub ecx, sizeof.TConfigHeader
mov [edx], ecx
add ecx, 4 ; .length field
stdcall DataHash, edx, ecx
mov [edx-4], eax ; -4 if the offset to TConfigHeader.hash relative to TConfigHeader.length
pop eax
pop ecx edx
return
endp
proc __DoSaveParamArray, .stream, .array
.count dd ?
begin
pushad
mov ebx, [.array]
mov ecx, [ebx+TArray.count]
lea ebx, [ebx+TArray.array-sizeof.TConfigRecord]
mov [.count], ecx
.loop:
add ebx, sizeof.TConfigRecord
dec [.count]
js .end_save
stdcall AddArrayItems, [.stream], 3
mov [.stream], edx
pushd [ebx+TConfigRecord.KeyName] [ebx+TConfigRecord.DataSize] [ebx+TConfigRecord.Type]
popd [eax+TChunkHeader.type] [eax+TChunkHeader.length] [eax+TChunkHeader.KeyName]
movzx eax, byte [ebx+TConfigRecord.Type]
and al, $07
movzx eax, [.type_handlers+eax]
add eax, .type_handlers
call eax
jc .loop
; align to dword
mov ecx, [ebx+TConfigRecord.DataSize]
add ecx, 3
and cl, $fc
shr ecx, 2
stdcall AddArrayItems, [.stream], ecx
mov [.stream], edx
mov edi, eax
rep movsd
jmp .loop
.end_save:
popad
mov edx, [.stream]
return
.type_handlers db .handle_null - .type_handlers
db .handle_int - .type_handlers
db .handle_string - .type_handlers
db .handle_blob - .type_handlers
db .handle_config - .type_handlers
db .handle_blob - .type_handlers
db .handle_blob - .type_handlers
db .handle_blob - .type_handlers
.handle_int:
lea esi, [ebx+TConfigRecord.Data]
clc
retn
.handle_string:
stdcall StrPtr, [ebx+TConfigRecord.Data]
mov esi, eax
clc
retn
.handle_blob:
mov esi, [ebx+TConfigRecord.Data]
clc
retn
.handle_config:
mov eax, [.stream]
mov eax, [eax+TArray.count]
push eax
stdcall __DoSaveParamArray, [.stream], [ebx+TConfigRecord.Data]
mov [.stream], edx
pop eax ; old size
mov ecx, [edx+TArray.count]
sub ecx, eax
lea edx, [edx+TArray.array+4*eax-sizeof.TChunkHeader]
shl ecx, 2
mov [edx+TChunkHeader.length], ecx
.handle_null:
stc
retn
endp
proc FreeConfigDB, .ptrRoot
.dummy TConfigRecord
begin
push eax
cmp [.ptrRoot], 0
je .exit
mov [.dummy.Type], cdtConfig
mov eax, [.ptrRoot]
mov [.dummy.Data], eax
lea eax, [.dummy]
stdcall __FreeConfigRecord, eax
.exit:
pop eax
return
endp
; searches the tree for the given path and returns a pointer to the variable, containing pointer to TArray of the directory.
proc __GetParamArray, .ptrVarRoot, .pDirectory, .create
begin
push ebx edx esi
mov esi, [.pDirectory]
mov ebx, [.ptrVarRoot]
test esi, esi
jz .found
.loop:
cmp dword [esi], 0
je .found
stdcall __ScanParamArray, [ebx], [esi]
jc .err_not_found
cmp [eax+TConfigRecord.Type], cdtConfig
jne .err_not_directory
.next:
lea ebx, [eax+TConfigRecord.Data]
add esi, 4
jmp .loop
.found:
mov eax, ebx
clc
pop esi edx ebx
return
.err_not_found:
cmp [.create], 0
je .no_create
stdcall AddArrayItems, [ebx], 1
mov [ebx], edx
mov edx, eax
pushd [esi]
popd [edx+TConfigRecord.KeyName]
mov [edx+TConfigRecord.Type], cdtConfig
stdcall CreateArray, sizeof.TConfigRecord
mov [edx+TConfigRecord.Data], eax
mov eax, edx
jmp .next
.no_create:
xor eax, eax
.error:
stc
pop esi edx ebx
return
.err_not_directory:
xor eax, eax
dec eax
jmp .error
endp
; returns:
; CF=0 and eax = pointer to TConfigRecord
; ecx = index of the record in the array.
; CF=1 and eax = 0 - the key was not found.
proc __ScanParamArray, .ptrArray, .name
begin
push ecx esi
mov esi, [.ptrArray]
test esi, esi
jz .not_found
mov ecx, [esi+TArray.count]
lea esi, [esi+TArray.array]
jecxz .not_found
xor edx, edx
.loop:
mov eax, [esi+TConfigRecord.KeyName]
cmp eax, [.name]
je .found
add esi, sizeof.TConfigRecord
inc edx
loop .loop
.not_found:
xor eax, eax
stc
pop esi ecx
return
.found:
mov eax, esi
clc
pop esi ecx
return
endp
; returns
; eax - poiner to the TConfigRecord or NULL if missing
proc GetConfigParam, .ptrConfig, .pDirectory, .name
begin
push edx
lea eax, [.ptrConfig]
stdcall __GetParamArray, eax, [.pDirectory], 0
stdcall __ScanParamArray, [eax], [.name]
pop edx
return
endp
proc GetConfigParam.AsString, .ptrConfig, .pDirectory, .name
begin
push edx
stdcall GetConfigParam, [.ptrConfig], [.pDirectory], [.name]
jc .finish
movzx edx, byte [eax+TConfigRecord.Type]
and dl, $07
movzx edx, [.type_handlers+edx]
add edx, .type_handlers
call edx
.finish:
pop edx
return
.type_handlers db .handle_null - .type_handlers
db .handle_int - .type_handlers
db .handle_string - .type_handlers
db .handle_blob - .type_handlers
db .handle_config - .type_handlers
db .handle_blob - .type_handlers
db .handle_blob - .type_handlers
db .handle_blob - .type_handlers
.handle_null:
stdcall StrDup, .txtNULL
retn
.handle_int:
stdcall NumToStr, [eax+TConfigRecord.Data], ntsSigned or ntsDec
retn
.handle_string:
stdcall StrDup, [eax+TConfigRecord.Data]
retn
.handle_config:
stdcall StrDup, .txtDir
retn
.handle_blob:
stdcall StrDup, .txtBlob
retn
.txtNULL db 'NULL', 0
.txtDir db 'SDIR', 0
.txtBlob db 'BLOB', 0
endp
proc __FreeConfigRecord, .ptrRecord
begin
pushad
mov esi, [.ptrRecord]
movzx eax, byte [esi+TConfigRecord.Type]
and al, $07
mov al, [.type_handlers+eax]
add eax, .type_handlers
call eax
xor eax, eax
mov [esi+TConfigRecord.Type], eax
mov [esi+TConfigRecord.Data], eax
mov [esi+TConfigRecord.DataSize], eax
popad
return
.type_handlers db .handle_null - .type_handlers
db .handle_int - .type_handlers
db .handle_string - .type_handlers
db .handle_blob - .type_handlers
db .handle_config - .type_handlers
db .handle_blob - .type_handlers
db .handle_blob - .type_handlers
db .handle_blob - .type_handlers
.handle_config:
mov eax, [esi+TConfigRecord.Data]
mov ecx, [eax+TArray.count]
lea eax, [eax+TArray.array]
jecxz .handle_blob
.loop:
stdcall __FreeConfigRecord, eax
add eax, sizeof.TConfigRecord
loop .loop
.handle_blob:
stdcall FreeMem, [esi+TConfigRecord.Data]
retn
.handle_string:
stdcall StrDel, [esi+TConfigRecord.Data]
.handle_null:
.handle_int:
retn
endp
proc DelCongigParam, .ptrVarConfig, .pDirectory, .name
begin
pushad
stdcall __GetParamArray, [.ptrVarConfig], [.pDirectory], 0
jc .deleted
mov esi, eax
stdcall __ScanParamArray, [esi], [.name]
jc .deleted
stdcall __FreeConfigRecord, eax
stdcall DeleteArrayItems, [esi], edx, 1
mov [esi], edx
.deleted:
popad
return
endp
; set the value of the given config parameter.
; if the parameter exists, the value will be changed.
; if the parameter does not exists, it will be created.
proc SetConfigParam, .ptrVarConfig, .pDirectory, .name, .type, .value, .size
begin
push eax ecx edx esi edi
stdcall __GetParamArray, [.ptrVarConfig], [.pDirectory], 1
jc .missing_directory
mov esi, eax
stdcall __ScanParamArray, [esi], [.name]
jnc .record_ok
stdcall AddArrayItems, [esi], 1
mov [esi], edx
push [.name]
pop [eax+TConfigRecord.KeyName]
.record_ok:
mov esi, eax
stdcall __FreeConfigRecord, esi
movzx ecx, byte [.type]
and cl, $7
mov [esi+TConfigRecord.Type], ecx
movzx ecx, [.type_handlers+ecx]
add ecx, .type_handlers
call ecx
clc
.finish:
pop edi esi edx ecx eax
return
.missing_directory:
stc
jmp .finish
.type_handlers db .handle_null - .type_handlers
db .handle_int - .type_handlers
db .handle_string - .type_handlers
db .handle_blob - .type_handlers
db .handle_config - .type_handlers
db .handle_blob - .type_handlers
db .handle_blob - .type_handlers
db .handle_blob - .type_handlers
.handle_int:
mov [esi+TConfigRecord.DataSize], 4
push [.value]
pop [esi+TConfigRecord.Data]
.handle_null:
retn
.handle_string:
stdcall StrDup, [.value]
mov [esi+TConfigRecord.Data], eax
stdcall StrLen, eax
mov [esi+TConfigRecord.DataSize], eax
retn
.handle_config:
stdcall CreateArray, sizeof.TConfigRecord
mov [esi+TConfigRecord.Data], eax
mov [esi+TConfigRecord.DataSize], 4
retn
.handle_blob:
mov ecx, [.size]
stdcall GetMem, ecx
mov [esi+TConfigRecord.Data], eax
mov [esi+TConfigRecord.DataSize], ecx
push esi
mov edi, eax
mov esi, [.value]
rep movsb
pop esi
retn
endp
endmodule