;[of]:hello
;[of]:license
;[c]fas2obj.asm - convert a fas file to a obj file with debug info
;[c]Copyright (C) 2009 Marc Kerbiquet
;[c]Based on pecvt by Sergey Choomak.
;[c]
;[c]Redistribution and use in source and binary forms, with or without
;[c]modification, are permitted provided that the following conditions are met:
;[c]
;[c]1. Redistributions of source code must retain the above copyright notice,
;[c]   this list of conditions and the following disclaimer.
;[c]2. Redistributions in binary form must reproduce the above copyright notice,
;[c]   this list of conditions and the following disclaimer in the documentation
;[c]   and/or other materials provided with the distribution.
;[c]
;[c]THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
;[c]AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
;[c]IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
;[c]DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
;[c]FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
;[c]DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
;[c]SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
;[c]CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
;[c]OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
;[c]OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;[c]
;[cf]
;[of]:documentation
;[c]Usage:
;[c]	fas2obj fas-file obj-file [debug-obj-file]
;[c]
;[c]INTERNALS
;[c]
;[c]Tips
;[c]	To browse this file, open it with Code Browser 
;[c]	(http://tibleiz.net/code-browser/), use the "Browser - Five Panes" 
;[c]	view and turn on the elastic tabstops mode.
;[c]
;[c]Calling conventions:
;[c]	* eax, ebx, ecx, edx are scatch registers.
;[c]	* esi, edi, ebp are preserved.
;[c]
;[c]Coding conventions:
;[c]	* Arguments are passed through eax, ebx, ecx and edx.
;[c]	* Values are returned using eax, ebx, ecx, edx and flags.
;[c]	* esi is usually used inside a method as the 'self' pointer.
;[c]
;[cf]
;[of]:imports

include "win32w.inc"
INVALID_SET_FILE_POINTER	=	-1
INVALID_FILE_SIZE	=	-1

macro imp name, size { extrn "__imp__" # `name #"@"# `size as name:DWORD }

;[c]format MS COFF
;[c]
;[c]	imp	GetCommandLineA, 0
;[c]	imp	ExitProcess, 4
;[c]	imp	GetLastError, 0
;[c]	imp	GetProcessHeap, 0
;[c]	imp	HeapAlloc, 12
;[c]	imp	HeapFree, 12
;[c]	imp	GlobalAlloc, 8
;[c]	imp	GlobalFree, 4
;[c]	imp	GlobalLock, 4
;[c]	imp	GlobalUnlock, 4
;[c]	imp	CreateFileA, 28
;[c]	imp	CloseHandle, 4
;[c]	imp	ReadFile, 20
;[c]	imp	WriteFile, 20
;[c]	imp	SetFilePointer, 16
;[c]	imp	GetFileSize, 8
;[c]	imp	GetStdHandle, 4
;[c]	
;[c]	include 'api\kernel32.inc'

format PE console 4.0
entry _start

section '.rdata' data readable writeable
data import
	library kernel32,'KERNEL32.DLL'
	include 'api\kernel32.inc'
end data
;[cf]
;[of]:macros
;[of]:private
macro private name
{
	label	name
}
;[cf]
;[of]:ro
struc ro name, n
{
	rd	0
	if n eq
		rb	sizeof.#name
	else
		rb	sizeof.#name * n
	end if
}
;[cf]
;[of]:struct
;[of]:struct
macro struct name, parent
{
	macro __end \{ sizeof.#name = pos \}
	macro __label a \{ label name \# a \}

	pos	=	0

	match any , parent \{ attr	_parent, parent \} 
}
;[cf]
;[of]:ends
macro ends
{
__end
	purge	__end
	purge	__label
}
;[cf]
;[of]:attr
macro attr name, type, cardinality
{
	local	n
	if cardinality eq
		n = 1
	else
		n = cardinality
	end if

	virtual at pos
	if type eq byte
		__label	.#name byte
		rb	n
	else if type eq word
		__label	.#name word
		rw	n
	else if type eq dword
		__label	.#name dword
		rd	n
	else if type eq qword
		__label	.#name qword
		rq	n
	else
		__label	.#name dword
		rb	sizeof.#type  * n
	end if
	pos	=	$
	end virtual
}
;[cf]
;[of]:ptr
macro ptr name, type, cardinality
{
	local	n
	if cardinality eq
		n = 1
	else
		n = cardinality
	end if

	virtual at pos
	__label	.#name dword
	rd	n
	pos	=	$
	end virtual
}
;[cf]
;[cf]
;[of]:locals
;[c]Declare local variables
;[c]
;[c]Example:
;[c]
;[c]main:	pushad
;[c]
;[c]	locals
;[c]	local	.x, dword
;[c]	local	.y, dword
;[c]	local	.z, box
;[c]	endl
;[c]
;[c]	mov	eax,[.x]
;[c]	mov	ebx,[.y]
;[c]	lea	ecx,[.z]
;[c]
;[c]	...
;[c]
;[c]	mov	esp,ebp
;[c]	popad
;[c]	ret
;[c]
macro locals
{
	mov	ebp,esp
zoffset	=	0
}

macro local name, type
{
	if	`type eq "dword"
zoffset	=	zoffset - 4
	virtual at ebp + zoffset
name	rd	1
	end virtual
	else if	`type eq "qword"
zoffset	=	zoffset - 8
	virtual at ebp + zoffset
name	rq	1
	end virtual
	else
zoffset	=	zoffset - ((sizeof.#type + 3) and not 3)
	virtual at ebp + zoffset
name	rd	0
	rb	sizeof.#type
	end virtual
	end if	
}

macro endl
{
	sub	esp, - zoffset
}

;[cf]
;[cf]
;[of]:bss
section '.bss' data readable writeable
;[c]	
;[c]	Memory manager
;[c]	
the_heap	ro	heap_memory
;[c]	
;[c]	Application
;[c]	
args	ro	command_line
fas_name	rd	1
obj_name	rd	1
dbg_name	rd	1
opt_help	rd	1
fas	ro	fas
obj	ro	obj
cv8	ro	cv8
debug_s	rd	1
;[cf]
;[cf]
;[of]:base
;[of]:bootstrap
section '.code' code readable executable

_start:	call	heap_initialize
	call	main
	
exit:	push	eax
	call	heap_release
	call	[ExitProcess]

memory_error:

	mov	eax, s_error_memory
	
fatal_error:

	push	eax
	mov	eax, s_error_prefix
	call	print
	pop	eax
	
	call	puts
	
	mov	eax, 1
	jmp	exit
;[cf]
;[of]:memory
;[of]:definitions
DEFAULT_HEAP_BLOCK_SIZE	=	128*1024
MAX_DELAYED_BLOCKS	=	32

;[c]
	struct	heap_memory
	attr	first_block,	dword	; First heap block
	attr	first_free,	dword	; First free block
	attr	last_freed,	dword	; The last freed block - used to optimize free-memory
	
	; The buffer to delay free-memory
	attr	delayed_size,	dword	; number of delayed entries
	attr	delayed,	delayed_block, MAX_DELAYED_BLOCKS	;
	
	; Statistics
	attr	total,	dword	; total num of alloc
	attr	left,	dword	; num of alloc - num of free
	attr	left_bytes,	dword	; num of left bytes
	attr	max_bytes,	dword	; max allocated memory
	ends

	struct	heap_block
	ptr	next,	heap_block
	ends

	struct	free_block
	ptr	next,	free_block	; Next free block or null
	attr	size,	dword	; Size of the block (with header)
	ends

	struct	delayed_block
	attr	base,	dword
	attr	limit,	dword
	ends
;[cf]
;[of]:module initialization
;[of]:initialize
heap_initialize:

	mov	eax, the_heap
	sub	ebx, ebx
	mov	[eax + heap_memory.first_block], ebx
	mov	[eax + heap_memory.first_free], ebx
	mov	[eax + heap_memory.last_freed], $FFFFFFFF
	mov	[eax + heap_memory.delayed_size], ebx
	mov	[eax + heap_memory.total], ebx
	mov	[eax + heap_memory.left], ebx
	mov	[eax + heap_memory.left_bytes], ebx
	mov	[eax + heap_memory.max_bytes], ebx
	ret
;[cf]
;[of]:release
heap_release:

	pushad
	mov	esi, the_heap

	call	flush_delayed
	
	; Get the process heap
	call	[GetProcessHeap]
	or	eax, eax
	jz	memory_error
	mov	ebx, eax

	; Free all allocated blocks
	mov	esi, [esi + heap_memory.first_block]
	jmp	.first
.next:	mov	edi, [esi + heap_block.next]
	sub	eax, eax
	push	esi		; pointer to the memory to free
	push	eax		; heap freeing flags
	push	ebx		; handle to the heap
	call	[HeapFree]
	mov	esi, edi
.first:	or	esi, esi
	jnz	.next
	
	popad
	ret
;[cf]
;[cf]
;[of]:allocating
;[of]:get memory
;[c]Allocate memory
;[c]
;[c]ARGUMENTS
;[c]	eax	size
;[c]
;[c]RETURN VALUES
;[c]	eax	address
;[c]
;[c]REMARKS
;[c]	This method does just an allocate_memory, but does not return
;[c]	if the allocation fails (the program exits immediately).
;[c]
get_memory:

	call	allocate_memory
	jz	memory_error
	ret
;[cf]
;[of]:get memory with size
;[c]Allocate memory block and saves the size
;[c]
;[c]ARGUMENTS
;[c]	eax	size
;[c]
;[c]RETURN VALUES
;[c]	eax	address
;[c]
;[c]REMARKS
;[c]	This method does just an allocate_memory_with_size, but does not return
;[c]	if the allocation fails (the program exits immediately).
;[c]
get_memory_with_size:

	call	allocate_memory_with_size
	jz	memory_error
	ret
;[cf]

;[of]:allocate memory with size
;[c]Allocate a memory block and saves the size
;[c]
;[c]ARGUMENTS
;[c]	eax	size
;[c]
;[c]RETURN VALUES
;[c]	eax	address
;[c]	zf	1 if not enough memory
;[c]
allocate_memory_with_size:

	add	eax, 4
	push	eax
	call	allocate_memory
	pop	ebx
	jz	return
	mov	[eax], ebx	; store raw size
	add	eax, 4	; skip raw size
return:	ret
;[cf]
;[of]:allocate memory
;[c]Allocate a memory block
;[c]
;[c]ARGUMENTS
;[c]	eax	size
;[c]
;[c]RETURN VALUES
;[c]	eax	address
;[c]	zf	1 if not enough memory
;[c]
allocate_memory:

	push	esi
	push	edi
	mov	esi, the_heap
	
	; Free optimization
	mov	[esi + heap_memory.last_freed], $FFFFFFFF
	cmp	[esi + heap_memory.delayed_size], 0
	jz	.empty
	push	eax
	call	flush_delayed
	pop	eax
.empty:

	; Update allocation counter
	inc	[esi + heap_memory.total]
	inc	[esi + heap_memory.left]
	add	[esi + heap_memory.left_bytes], eax
	mov	ebx, [esi + heap_memory.left_bytes]
	cmp	ebx, [esi + heap_memory.max_bytes]
	jc	.not_max
	mov	[esi + heap_memory.max_bytes], ebx
.not_max:
	
	;
	; The size is 8-byte aligned
	;
	add	eax, sizeof.free_block - 1
	and	eax, not (sizeof.free_block - 1)

	;
	; Search a free block large enough
	;
	lea	edx, [esi + heap_memory.first_free]
	jmp	.first
.next:	cmp	[edi + free_block.size], eax
	jnc	.found
	lea	edx, [edi + free_block.next]
.first:	mov	edi, [edx]
	or	edi, edi
	jnz	.next
	
	;
	; No block found: we must ask the operating system
	; for a new heap block.
	;
	call	create_heap_block
	jz	.done

	;
	; Found a block
	;
	;	esi	the heap
	;	eax	the size to allocate
	;	edx	pointer to this free block (previous link)
	;	edi	the free block
	;
.found:	mov	ebx, [edi + free_block.next]
	mov	ecx, [edi + free_block.size]
	sub	ecx, eax
	jz	.full
	add	edi, eax
	mov	[edi + free_block.next], ebx
	mov	[edi + free_block.size], ecx
	mov	ebx, edi
	sub	edi, eax

.full:	mov	[edx], ebx
	mov	eax, edi

	or	eax, eax
.done:	pop	edi
	pop	esi
	ret


;[c]
;[c]
;[c]ARGUMENTS
;[c]	esi	the heap
;[c]	eax	minimum size to allocate
;[c]
;[c]RETURN VALUES
;[c]	esi	the heap
;[c]	eax	minimum size to allocate
;[c]	edx	pointer to this free block (previous link)
;[c]	edi	the free block
;[c]	
private create_heap_block

	push	eax

	;
	; Allocate max(2*size, default_size)
	;
	mov	ebx, DEFAULT_HEAP_BLOCK_SIZE
	shl	eax, 1
	cmp	eax, ebx
	jnc	.1
	mov	eax, ebx
.1:	add	eax, sizeof.heap_block

	;
	; Allocate the block
	;
	mov	ebx, eax
	call	[GetProcessHeap]
	or	eax, eax
	jz	memory_error
	sub	ecx, ecx
	push	ebx		; number of bytes to allocate
	push	ecx		; heap allocation control flags
	push	eax		; handle to the private heap block
	call	[HeapAlloc]
	or	eax, eax
	jz	.failed

	mov	edi, eax
	
	;
	; Attach the block
	;
	mov	eax, [esi + heap_memory.first_block]
	mov	[edi + heap_block.next], eax
	mov	[esi + heap_memory.first_block], edi

	;
	; Deduce the header
	;
	add	edi, sizeof.heap_block
	sub	ebx, sizeof.heap_block

	;
	; Now this free block must be inserted in the chain
	; of free blocks at the right position
	;
	lea	edx, [esi + heap_memory.first_free]
	jmp	.first
.next:	cmp	eax, edi
	ja	.found
	lea	edx, [eax + free_block.next]
.first:	mov	eax, [edx]
	or	eax, eax
	jnz	.next
.found:

	;
	; Initialize the free block
	;
	mov	eax, [edx]
	mov	[edi + free_block.next], eax
	mov	[edi + free_block.size], ebx

	or	esi, esi
.failed:
	pop	eax
	ret
;[cf]
;[of]:free memory with size
;[c]Free a memory block allocated with allocate_memory_with_size
;[c]
;[c]ARGMENTS
;[c]	eax	the memory block or null
;[c]
;[c]REMARKS
;[c]	The method has no effect if the pointer is null.
;[c]
free_memory_with_size:

	or	eax, eax
	jz	return

	sub	eax, 4
	mov	ebx, [eax]
	;jmp	free_memory	; free_memory is just below
;[cf]
;[of]:free memory
;[c]Free a memory block
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory block
;[c]	ebx	the size of the block
;[c]
;[c]REMARKS
;[c]	Freeing a null pointer has no effect.
;[c]
free_memory:

	or	eax, eax
	jz	return
	
	push	esi
	mov	esi, the_heap
	
	; Update allocation counter
	dec	[esi + heap_memory.left]
	sub	[esi + heap_memory.left_bytes], ebx
	
	; Flush the delayed blocks if the queue is full
	cmp	[esi + heap_memory.delayed_size], MAX_DELAYED_BLOCKS
	jnz	.not_full
	push	eax
	push	ebx
	call	flush_delayed
	pop	ebx
	pop	eax
.not_full:	
	
	; Try to merge the block with an existing one
	add	ebx, eax
	lea	edx, [esi + heap_memory.delayed]
	mov	ecx, [esi + heap_memory.delayed_size]
	jecxz	.not_found
.loop:	cmp	ebx, [edx + delayed_block.base]
	jz	.before
	jb	.insert
	cmp	eax, [edx + delayed_block.limit]
	jz	.after
	add	edx, sizeof.delayed_block
	loop	.loop
	jmp	.not_found
	
	; Insert the block here
.insert:	push	eax
	push	ebx
	push	edx
	lea	eax, [edx + sizeof.delayed_block]
	mov	ebx, edx
	shl	ecx, 3	; * sizeof.delayed_block
	call	memory_move
	pop	edx
	pop	ebx
	pop	eax
	
	; Add a new block
.not_found:	mov	[edx + delayed_block.base], eax
	mov	[edx + delayed_block.limit], ebx
	inc	[esi + heap_memory.delayed_size]
	jmp	.done
	
	; Merge with next
.before:	mov	[edx + delayed_block.base], eax
	jmp	.done
	
	; Merge with previous
.after:	mov	[edx + delayed_block.limit], ebx
	
.done:	pop	esi
	ret

;[cf]
;[cf]
;[of]:copying
;[of]:copy
;[c]Copy a memory block
;[c]
;[c]ARGUMENTS
;[c]	eax	the target
;[c]	ebx	the source
;[c]	ecx	the size in bytes
;[c]
;[c]RETURN VALUES
;[c]	eax	the end of the target
;[c]
memory_copy:

	push	esi
	push	edi
	mov	edi, eax
	mov	esi, ebx
	push	ecx
	shr	ecx, 2
	rep	movsd
	pop	ecx
	and	ecx, 3
	rep	movsb
	mov	eax, edi
	pop	edi
	pop	esi
	ret
;[cf]
;[of]:move
;[c]Moves a memory block
;[c]
;[c]ARGUMENTS
;[c]	eax	the target
;[c]	ebx	the source
;[c]	ecx	the size in bytes
;[c]
memory_move:

	cmp	eax, ebx
	jz	.done
	jc	memory_copy
	lea	edx, [ebx + ecx]
	cmp	eax, edx
	jnc	memory_copy

	push	esi
	push	edi
	lea	esi, [ebx + ecx - 4]
	lea	edi, [eax + ecx - 4]
	std
	push	ecx
	shr	ecx, 2
	rep	movsd
	pop	ecx
	and	ecx, 3
	add	esi, 3
	add	edi, 3
	rep	movsb
	cld	
	pop	edi
	pop	esi

.done:	ret
;[cf]
;[of]:clear
;[c]Fill a memory block with zeroes
;[c]
;[c]ARGUMENTS
;[c]	eax	the base address
;[c]	ebx	the number of bytes
;[c]
memory_clear:

	push	edi
	mov	edi, eax
	sub	eax, eax
	mov	ecx, ebx
	rep	stosb
	pop	edi
	ret
;[cf]
;[cf]
;[of]:comparing
;[of]:compare
;[c]Compare two memory block
;[c]
;[c]ARGUMENTS
;[c]	eax	the first block
;[c]	ebx	the second block
;[c]	ecx	the size in bytes
;[c]
;[c]RETURN VALUES
;[c]	zf	1 if equals
;[c]
;[c]REMARKS
;[c]	Very naive implementation.
;[c]
memory_compare:

	or	ecx, ecx	; zf = 1 if size is null
	jz	.done
.loop:	mov	dl, [eax]
	cmp	dl, [ebx]
	jnz	.done
	add	eax, 1
	add	ebx, 1
	loop	.loop
	sub	eax, eax	; zf = 1
.done:	ret
;[cf]
;[cf]

;[of]:checksum
;[of]:get crc32
;[c]Compute the CRC32 of a memory block
;[c]
;[c]ARGUMENTS
;[c]	eax	the beginning of the memory block
;[c]	ebx	the block size
;[c]
;[c]RETURN VALUES
;[c]	eax	the crc32
;[c]
get_crc32:

	push	esi
	
	mov	esi, eax
	lea	edx, [esi + ebx]
	mov	eax, -1
	jmp	.first
	
.loop:	sub	ecx, ecx
	mov	bl, byte [esi]
	add	esi, 1
	mov	cl, al
	xor	cl, bl
	shr	eax, 8
	xor	eax, [crc32_table + ecx * 4]
.first:	cmp	edx, esi
	jnz	.loop
	
	not	eax
	pop	esi
	ret

crc32_table:

	dd	$00000000,	$77073096,	$EE0E612C,	$990951BA
	dd	$076DC419,	$706AF48F,	$E963A535,	$9E6495A3
	dd	$0EDB8832,	$79DCB8A4,	$E0D5E91E,	$97D2D988
	dd	$09B64C2B,	$7EB17CBD,	$E7B82D07,	$90BF1D91
	dd	$1DB71064,	$6AB020F2,	$F3B97148,	$84BE41DE
	dd	$1ADAD47D,	$6DDDE4EB,	$F4D4B551,	$83D385C7
	dd	$136C9856,	$646BA8C0,	$FD62F97A,	$8A65C9EC
	dd	$14015C4F,	$63066CD9,	$FA0F3D63,	$8D080DF5
	dd	$3B6E20C8,	$4C69105E,	$D56041E4,	$A2677172
	dd	$3C03E4D1,	$4B04D447,	$D20D85FD,	$A50AB56B
	dd	$35B5A8FA,	$42B2986C,	$DBBBC9D6,	$ACBCF940
	dd	$32D86CE3,	$45DF5C75,	$DCD60DCF,	$ABD13D59
	dd	$26D930AC,	$51DE003A,	$C8D75180,	$BFD06116
	dd	$21B4F4B5,	$56B3C423,	$CFBA9599,	$B8BDA50F
	dd	$2802B89E,	$5F058808,	$C60CD9B2,	$B10BE924
	dd	$2F6F7C87,	$58684C11,	$C1611DAB,	$B6662D3D
	dd	$76DC4190,	$01DB7106,	$98D220BC,	$EFD5102A
	dd	$71B18589,	$06B6B51F,	$9FBFE4A5,	$E8B8D433
	dd	$7807C9A2,	$0F00F934,	$9609A88E,	$E10E9818
	dd	$7F6A0DBB,	$086D3D2D,	$91646C97,	$E6635C01
	dd	$6B6B51F4,	$1C6C6162,	$856530D8,	$F262004E
	dd	$6C0695ED,	$1B01A57B,	$8208F4C1,	$F50FC457
	dd	$65B0D9C6,	$12B7E950,	$8BBEB8EA,	$FCB9887C
	dd	$62DD1DDF,	$15DA2D49,	$8CD37CF3,	$FBD44C65
	dd	$4DB26158,	$3AB551CE,	$A3BC0074,	$D4BB30E2
	dd	$4ADFA541,	$3DD895D7,	$A4D1C46D,	$D3D6F4FB
	dd	$4369E96A,	$346ED9FC,	$AD678846,	$DA60B8D0
	dd	$44042D73,	$33031DE5,	$AA0A4C5F,	$DD0D7CC9
	dd	$5005713C,	$270241AA,	$BE0B1010,	$C90C2086
	dd	$5768B525,	$206F85B3,	$B966D409,	$CE61E49F
	dd	$5EDEF90E,	$29D9C998,	$B0D09822,	$C7D7A8B4
	dd	$59B33D17,	$2EB40D81,	$B7BD5C3B,	$C0BA6CAD
	dd	$EDB88320,	$9ABFB3B6,	$03B6E20C,	$74B1D29A
	dd	$EAD54739,	$9DD277AF,	$04DB2615,	$73DC1683
	dd	$E3630B12,	$94643B84,	$0D6D6A3E,	$7A6A5AA8
	dd	$E40ECF0B,	$9309FF9D,	$0A00AE27,	$7D079EB1
	dd	$F00F9344,	$8708A3D2,	$1E01F268,	$6906C2FE
	dd	$F762575D,	$806567CB,	$196C3671,	$6E6B06E7
	dd	$FED41B76,	$89D32BE0,	$10DA7A5A,	$67DD4ACC
	dd	$F9B9DF6F,	$8EBEEFF9,	$17B7BE43,	$60B08ED5
	dd	$D6D6A3E8,	$A1D1937E,	$38D8C2C4,	$4FDFF252
	dd	$D1BB67F1,	$A6BC5767,	$3FB506DD,	$48B2364B
	dd	$D80D2BDA,	$AF0A1B4C,	$36034AF6,	$41047A60
	dd	$DF60EFC3,	$A867DF55,	$316E8EEF,	$4669BE79
	dd	$CB61B38C,	$BC66831A,	$256FD2A0,	$5268E236
	dd	$CC0C7795,	$BB0B4703,	$220216B9,	$5505262F
	dd	$C5BA3BBE,	$B2BD0B28,	$2BB45A92,	$5CB36A04
	dd	$C2D7FFA7,	$B5D0CF31,	$2CD99E8B,	$5BDEAE1D
	dd	$9B64C2B0,	$EC63F226,	$756AA39C,	$026D930A
	dd	$9C0906A9,	$EB0E363F,	$72076785,	$05005713
	dd	$95BF4A82,	$E2B87A14,	$7BB12BAE,	$0CB61B38
	dd	$92D28E9B,	$E5D5BE0D,	$7CDCEFB7,	$0BDBDF21
	dd	$86D3D2D4,	$F1D4E242,	$68DDB3F8,	$1FDA836E
	dd	$81BE16CD,	$F6B9265B,	$6FB077E1,	$18B74777
	dd	$88085AE6,	$FF0F6A70,	$66063BCA,	$11010B5C
	dd	$8F659EFF,	$F862AE69,	$616BFFD3,	$166CCF45
	dd	$A00AE278,	$D70DD2EE,	$4E048354,	$3903B3C2
	dd	$A7672661,	$D06016F7,	$4969474D,	$3E6E77DB
	dd	$AED16A4A,	$D9D65ADC,	$40DF0B66,	$37D83BF0
	dd	$A9BCAE53,	$DEBB9EC5,	$47B2CF7F,	$30B5FFE9
	dd	$BDBDF21C,	$CABAC28A,	$53B39330,	$24B4A3A6
	dd	$BAD03605,	$CDD70693,	$54DE5729,	$23D967BF
	dd	$B3667A2E,	$C4614AB8,	$5D681B02,	$2A6F2B94
	dd	$B40BBE37,	$C30C8EA1,	$5A05DF1B,	$2D02EF8D
;[cf]
;[of]:get md5
;[c]Compute the MD5 of a memory block
;[c]
;[c]ARGUMENTS
;[c]	eax	the buffer
;[c]	ebx	the size of the buffer
;[c]	ecx	the buffer to store result (16 bytes)
;[c]
;[c]REMARKS
;[c]	This code is a bit tricky because it uses all registers to minimize 
;[c]	memory accesses during computation:
;[c]	* eax...edx stores the intermediary state
;[c]	* esi and edi are temporary register for computations
;[c]	* ebp is the pointer to data
;[c]	* the loop counter is on the stack.
;[c]
	struct	md5digest
	attr	bytes,	byte, 16
	ends

get_md5:

A	equ	esp + 0
B	equ	esp + 4
C	equ	esp + 8
D	equ	esp + 12
md5_output	equ	esp + 16
md5_p	equ	esp + 20
md5_n	equ	esp + 24
md5_padn	equ	esp + 28
md5_i	equ	esp + 32
md5_padding	equ	esp + 36
md5_ssize	equ	36 + 128

	pushad
	sub	esp, md5_ssize

	mov	[md5_p], eax
	mov	[md5_output], ecx
	mov	ecx, ebx
	shr	ecx, 6
	mov	[md5_n], ecx
	mov	dword [md5_padn], 1

	; Pointer to last bytes
	mov	ecx, ebx
	and	ecx, not 63
	lea	esi, [eax + ecx]
	
	; Number of bytes
	mov	ecx, ebx
	and	ecx, 63
	
	; Copy bytes
	lea	edi, [md5_padding]
	rep	movsb
	
	; Append $80
	mov	al, $80
	stosb
	
	; How many zeroes ?
	lea 	ecx, [md5_padding + 56]
	cmp	edi, ecx
	jbe	.one
	add	ecx, 64
	inc	dword [md5_padn]
.one:	sub	ecx, edi

	; Fill with zeroes
	sub	eax, eax
	rep	stosb
	
	; Append the size in bits to the end
	mov	eax, 8
	mul	ebx
	mov	[edi],eax
	mov	[edi + 4],edx

	; Initialize
	mov	dword [A], $67452301
	mov	dword [B], $EFCDAB89
	mov	dword [C], $98BADCFE
	mov	dword [D], $10325476
	
	; Main block
	mov	ebp, [md5_p]
	mov	eax, [md5_n]
	call	md5_update
	
	; Remaining data
	lea	ebp, [md5_padding]
	mov	eax, [md5_padn]
	call	md5_update

	; Copy result
	lea	esi, [A]
	mov	edi, [md5_output]
	mov	ecx, 4
	rep	movsd

	add	esp, md5_ssize
	popad
	ret

;[of]:md5 update
;[c]Compute md5 on a block
;[c]
;[c]ARGUMENTS
;[c]	esp	locals
;[c]	ebp	the pointer
;[c]	eax	number of blocks
;[c]
md5_update:

	or	eax, eax
	jz	.done

	mov	[md5_i + 4], eax
.loop:	mov	eax, [A + 4]
	mov	ebx, [B + 4]
	mov	ecx, [C + 4]
	mov	edx, [D + 4]
;[of]:	Round 1
macro FF a, b, c, d, x, s, ac
{
	mov	esi, b
	and	esi, c
	mov	edi, b
	not	edi
	and	edi, d
	or	esi, edi
	add	a, esi
	add	a, [ebp + (x * 4)]
	add	a, ac
	rol	a, s
	add	a, b
}

	FF	eax,	ebx,	ecx,	edx,	0,	7,	$d76aa478
	FF	edx,	eax,	ebx,	ecx,	1,	12,	$e8c7b756
	FF	ecx,	edx,	eax,	ebx,	2,	17,	$242070db
	FF	ebx,	ecx,	edx,	eax,	3,	22,	$c1bdceee
	FF	eax,	ebx,	ecx,	edx,	4,	7,	$f57c0faf
	FF	edx,	eax,	ebx,	ecx,	5,	12,	$4787c62a
	FF	ecx,	edx,	eax,	ebx,	6,	17,	$a8304613
	FF	ebx,	ecx,	edx,	eax,	7,	22,	$fd469501
	FF	eax,	ebx,	ecx,	edx,	8,	7,	$698098d8
	FF	edx,	eax,	ebx,	ecx,	9,	12,	$8b44f7af
	FF	ecx,	edx,	eax,	ebx,	10,	17,	$ffff5bb1
	FF	ebx,	ecx,	edx,	eax,	11,	22,	$895cd7be
	FF	eax,	ebx,	ecx,	edx,	12,	7,	$6b901122
	FF	edx,	eax,	ebx,	ecx,	13,	12,	$fd987193
	FF	ecx,	edx,	eax,	ebx,	14 ,	17,	$a679438e
	FF	ebx,	ecx,	edx,	eax,	15 ,	22,	$49b40821
;[cf]
;[of]:	Round 2
macro GG a, b, c, d, x, s, ac
{
	mov	esi, b
	and	esi, d
	mov	edi, d
	not	edi
	and	edi, c
	or	esi, edi
	add	a, esi
	add	a, [ebp + (x * 4)]
	add	a, ac
	rol	a, s
	add	a, b
}
 
 
	GG	eax,	ebx,	ecx,	edx,	01,	05,	$f61e2562
	GG	edx,	eax,	ebx,	ecx,	06,	09,	$c040b340
	GG	ecx,	edx,	eax,	ebx,	11,	14,	$265e5a51
	GG	ebx,	ecx,	edx,	eax,	00,	20,	$e9b6c7aa
	GG	eax,	ebx,	ecx,	edx,	05,	05,	$d62f105d
	GG	edx,	eax,	ebx,	ecx,	10,	09,	$02441453
	GG	ecx,	edx,	eax,	ebx,	15,	14,	$d8a1e681
	GG	ebx,	ecx,	edx,	eax,	04,	20,	$e7d3fbc8
	GG	eax,	ebx,	ecx,	edx,	09,	05,	$21e1cde6
	GG	edx,	eax,	ebx,	ecx,	14,	09,	$c33707d6
	GG	ecx,	edx,	eax,	ebx,	03,	14,	$f4d50d87
	GG	ebx,	ecx,	edx,	eax,	08,	20,	$455a14ed
	GG	eax,	ebx,	ecx,	edx,	13,	05,	$a9e3e905
	GG	edx,	eax,	ebx,	ecx,	02,	09,	$fcefa3f8
	GG	ecx,	edx,	eax,	ebx,	07,	14,	$676f02d9
	GG	ebx,	ecx,	edx,	eax,	12,	20,	$8d2a4c8a
;[cf]
;[of]:	Round 3
macro HH a, b, c, d, x, s, ac
{
	mov	esi, b
	xor	esi, c
	xor	esi, d
	add	a, esi
	add	a, [ebp + (x * 4)]
	add	a, ac
	rol	a, s
	add	a, b
}

	HH	eax,	ebx,	ecx,	edx,	05,	04,	$fffa3942
	HH	edx,	eax,	ebx,	ecx,	08,	11,	$8771f681
	HH	ecx,	edx,	eax,	ebx,	11,	16,	$6d9d6122
	HH	ebx,	ecx,	edx,	eax,	14,	23,	$fde5380c
	HH	eax,	ebx,	ecx,	edx,	01,	04,	$a4beea44
	HH	edx,	eax,	ebx,	ecx,	04,	11,	$4bdecfa9
	HH	ecx,	edx,	eax,	ebx,	07,	16,	$f6bb4b60
	HH	ebx,	ecx,	edx,	eax,	10,	23,	$bebfbc70
	HH	eax,	ebx,	ecx,	edx,	13,	04,	$289b7ec6
	HH	edx,	eax,	ebx,	ecx,	00,	11,	$eaa127fa
	HH	ecx,	edx,	eax,	ebx,	03,	16,	$d4ef3085
	HH	ebx,	ecx,	edx,	eax,	06,	23,	$04881d05
	HH	eax,	ebx,	ecx,	edx,	09,	04,	$d9d4d039
	HH	edx,	eax,	ebx,	ecx,	12,	11,	$e6db99e5
	HH	ecx,	edx,	eax,	ebx,	15,	16,	$1fa27cf8
	HH	ebx,	ecx,	edx,	eax,	02,	23,	$c4ac5665
;[cf]
;[of]:	Round 4
macro II a, b, c, d, x, s, ac
{
	mov	esi, d
	not	esi
	or	esi, b
	xor	esi, c
	add	a, esi
	add	a, [ebp + (x * 4)]
	add	a, ac
	rol	a, s
	add	a, b
}

	II	eax,	ebx,	ecx,	edx,	00,	06,	$f4292244
	II	edx,	eax,	ebx,	ecx,	07,	10,	$432aff97
	II	ecx,	edx,	eax,	ebx,	14,	15,	$ab9423a7
	II	ebx,	ecx,	edx,	eax,	05,	21,	$fc93a039
	II	eax,	ebx,	ecx,	edx,	12,	06,	$655b59c3
	II	edx,	eax,	ebx,	ecx,	03,	10,	$8f0ccc92
	II	ecx,	edx,	eax,	ebx,	10,	15,	$ffeff47d
	II	ebx,	ecx,	edx,	eax,	01,	21,	$85845dd1
	II	eax,	ebx,	ecx,	edx,	08,	06,	$6fa87e4f
	II	edx,	eax,	ebx,	ecx,	15,	10,	$fe2ce6e0
	II	ecx,	edx,	eax,	ebx,	06,	15,	$a3014314
	II	ebx,	ecx,	edx,	eax,	13,	21,	$4e0811a1
	II	eax,	ebx,	ecx,	edx,	04,	06,	$f7537e82
	II	edx,	eax,	ebx,	ecx,	11,	10,	$bd3af235
	II	ecx,	edx,	eax,	ebx,	02,	15,	$2ad7d2bb
	II	ebx,	ecx,	edx,	eax,	09,	21,	$eb86d391
;[cf]
	add	[A + 4], eax
	add	[B + 4], ebx
	add	[C + 4], ecx
	add	[D + 4], edx
	add	ebp, 64
	dec	dword [md5_i + 4]
	jnz	.loop

.done:	ret
;[cf]
;[cf]
;[cf]
;[of]:private
;[of]:flush delayed
;[c]Flush delayed blocks
;[c]
;[c]ARGUMENTS
;[c]	esi	the heap memory
;[c]
private flush_delayed

	push	edi
	push	ebp
	mov	eax, [esi + heap_memory.delayed_size]
	lea	edi, [esi + heap_memory.delayed]
	lea	ebp, [edi + eax * sizeof.delayed_block]
	jmp	.first
.loop:	mov	eax, [edi + delayed_block.base]
	mov	ebx, [edi + delayed_block.limit]
	sub	ebx, eax
	call	real_free_memory
	add	edi, sizeof.delayed_block
.first:	cmp	edi, ebp
	jnz	.loop
	
	mov	[esi + heap_memory.delayed_size], 0
	pop	ebp
	pop	edi
	ret

;[of]:real free memory
;[c]Free a memory block
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory block
;[c]	ebx	the size of the block
;[c]	esi	the heap memory
;[c]
private real_free_memory

	; The size is 8-byte aligned
	add	ebx, sizeof.free_block - 1
	and	ebx, not (sizeof.free_block - 1)

	; Search the previous block
	;
	
	; Optimization: start from the previous freed block
	; Often, several contiguous blocks are freed
	mov	ecx, [esi + heap_memory.last_freed]
	cmp	ecx, eax
	jb	.next
	
	; Search from the first free blocks until pointer is
	; over the block to free (free blocks are sorted)
	mov	ecx, [esi + heap_memory.first_free]	
	or	ecx, ecx	; No free block
	jz	.first	;
	cmp	ecx, eax	; The first free block is after
	ja	.first	;
			
.next:	mov	edx, [ecx + free_block.next]	
	or	edx, edx	; No next free block
	jz	.append_or_merge	;
	cmp	edx, eax	; The next free block is after
	ja	.append_or_merge	;
	mov	ecx, edx
	jmp	.next

	;
	; The freed block is the first one
	;
.first:	mov	edx, [esi + heap_memory.first_free]
	mov	[eax + free_block.next], edx
	mov	[eax + free_block.size], ebx
	mov	[esi + heap_memory.first_free], eax
	jmp	.check_with_next
	
	;
	; Insert or merge with previous
	;
	;	ecx	the previous block
	;

.append_or_merge:

	mov	edx, ecx
	add	edx, [ecx + free_block.size]
	cmp	edx, eax
	jz	.merge_with_previous

	mov	edx, [ecx + free_block.next]
	mov	[eax + free_block.next], edx
	mov	[eax + free_block.size], ebx
	mov	[ecx + free_block.next], eax
	jmp	.check_with_next

.merge_with_previous:

	add	[ecx + free_block.size], ebx	; Add the size of the newly freed block
	mov	eax, ecx	; The previous block become the current one
	
	;
	; Merge with next
	;
	;	eax	the current block to merge with next if possible
	;
.check_with_next:
	mov	ebx, eax
	add	ebx, [eax + free_block.size]
	mov	ecx, [eax + free_block.next]
	cmp	ecx, ebx
	jnz	.not_contiguous
	
	mov	ebx, [ecx + free_block.next]
	mov	ecx, [ecx + free_block.size]
	mov	[eax + free_block.next], ebx
	add	[eax + free_block.size], ecx
	
.not_contiguous:

	mov	[esi + heap_memory.last_freed], eax
	ret
;[cf]
;[cf]
;[cf]
;[cf]
;[of]:memory buffer
;[of]:definitions
	struct	memory_buffer
	ptr	p,	byte
	attr	used,	dword		; used bytes
	attr	buffer_size,	dword		; size of buffer in bytes
	ends
;[cf]
;[of]:initialize - release
;[of]:initialize
;[c]Initialize a memory buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]
memory_buffer_initialize:

	sub	ebx, ebx
	mov	[eax + memory_buffer.p], ebx
	mov	[eax + memory_buffer.used], ebx
	mov	[eax + memory_buffer.buffer_size], ebx
	ret
;[cf]
;[of]:release
;[c]Release the memory buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]
memory_buffer_release:

	mov	ebx, [eax + memory_buffer.buffer_size]
	mov	eax, [eax + memory_buffer.p]
	jmp	free_memory
;[cf]
;[of]:copy
;[c]Copy a memory buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]	ebx	the source memory buffer
;[c]
memory_buffer_copy:

	push	esi
	push	edi
	mov	esi, eax
	mov	edi, ebx
	
	mov	eax, [edi + memory_buffer.used]
	call	get_memory
	mov	[esi + memory_buffer.p], eax
	mov	ecx, [edi + memory_buffer.used]
	mov	[esi + memory_buffer.buffer_size], ecx
	mov	[esi + memory_buffer.used], ecx
	
	mov	ebx, [edi + memory_buffer.p]
	call	memory_copy

	pop	edi
	pop	esi
	ret
;[cf]
;[cf]
;[of]:adding - removing
;[of]:add string
;[c]Append a 8-bit string to the memory buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]	ebx	the 8-bit string
;[c]
;[c]REMARKS
;[c]	The ending zero is not append.
;[c]
memory_buffer_add_string:

	; Compute size of the string => ecx
	push	eax
	push	esi
	mov	esi, ebx
.loop:	lodsb
	or	al, al
	jnz	.loop
	sub	esi, ebx
	lea	ecx, [esi - 1]
	pop	esi
	pop	eax

	jmp	memory_buffer_add_bytes
;[cf]
;[of]:add bytes
;[c]Append a memory block to the memory buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]	ebx	the address of the memory block
;[c]	ecx	the size of the memory block
;[c]
;[c]REMARKS
;[c]	A null size is valid.
;[c]
memory_buffer_add_bytes:

	push	esi edi ebp
	mov	esi, eax
	mov	edi, ebx
	mov	ebp, ecx

	; Reserve space
	mov	ebx, ecx
	mov	eax, esi
	call	memory_buffer_reserve
	
	; Copy data
	mov	eax, [esi + memory_buffer.p]
	add	eax, [esi + memory_buffer.used]
	mov	ebx, edi
	mov	ecx, ebp
	call	memory_copy

	; Upate used counter
	add	[esi + memory_buffer.used], ebp
	
	pop	ebp edi esi
	ret
;[cf]
;[of]:add byte
;[c]Append a byte to the memory buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]	bl	the char
;[c]
memory_buffer_add_byte:

	push	esi
	mov	esi, eax

	push	ebx
	mov	ebx, 1		; size of byte
	call	memory_buffer_reserve
	pop	ebx
	
	mov	eax, [esi + memory_buffer.p]
	add	eax, [esi + memory_buffer.used]
	mov	[eax], bl

	add	[esi + memory_buffer.used], 1	; size of byte
	
	pop	esi
	ret
;[cf]
;[of]:add word
;[c]Append a word to the memory buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]	bx	the word
;[c]
memory_buffer_add_word:

	push	esi
	mov	esi, eax

	push	ebx
	mov	ebx, 2		; size of word
	call	memory_buffer_reserve
	pop	ebx
	
	mov	eax, [esi + memory_buffer.p]
	add	eax, [esi + memory_buffer.used]
	mov	[eax], bx

	add	[esi + memory_buffer.used], 2	; size of byte
	
	pop	esi
	ret
;[cf]
;[of]:add dword
;[c]Append a dword to the memory buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]	ebx	the long word
;[c]
memory_buffer_add_dword:

	push	esi
	mov	esi, eax

	push	ebx
	mov	ebx, 4		; size of dword
	call	memory_buffer_reserve
	pop	ebx
	
	mov	eax, [esi + memory_buffer.p]
	add	eax, [esi + memory_buffer.used]
	mov	[eax], ebx

	add	[esi + memory_buffer.used], 4	; size of dword
	
	pop	esi
	ret
;[cf]

;[of]:reserve
;[c]Reserve some chars
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]	ebx	the size to reserve in bytes
;[c]
memory_buffer_reserve:

	mov	ecx, ebx
	add	ecx, [eax + memory_buffer.used]
	cmp	ecx, [eax + memory_buffer.buffer_size]
	jle	.done

	push	esi
	push	edi
	mov	esi, eax

	; Compute new size
	lea	eax, [ecx*2 + ecx]	; * 3
	shr	eax, 1	; / 2
	and	eax, not 1	; must be pair (for string buffer)
	;add	eax, DEFAULT_MEMORY_BUFFER_SIZE	; minimum add
	push	eax
	
	; Allocate new buffer
	call	get_memory
	push	eax

	; Move buffer
	mov	ebx, [esi + memory_buffer.p]
	mov	ecx, [esi + memory_buffer.used]
	call	memory_copy
	
	; Free the old buffer
	mov	eax, [esi + memory_buffer.p]
	mov	ebx, [esi + memory_buffer.buffer_size]
	call	free_memory

	; Update object
	pop	[esi + memory_buffer.p]
	pop	[esi + memory_buffer.buffer_size]

	pop	edi
	pop	esi
.done:	ret
;[cf]

;[of]:remove all
;[c]Empty the content of the buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]
memory_buffer_remove_all:

	sub	ebx, ebx
	mov	[eax + memory_buffer.used], ebx
	ret
;[cf]
;[of]:remove
;[c]Removes a range
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]	ebx	the offset in bytes
;[c]	ecx	number of bytes to remove
;[c]
memory_buffer_remove:

	pushad
	mov	esi, eax
	mov	edi, ecx

	; move the end to overlap the range to be removed
	lea	edx, [ebx + ecx]
	mov	eax, [esi + memory_buffer.p]
	add	eax, ebx
	mov	ebx, [esi + memory_buffer.p]
	add	ebx, edx
	mov	ecx, [esi + memory_buffer.used]
	sub	ecx, edx
	call	memory_move

	; update the used bytes
	sub	[esi + memory_buffer.used], edi
	
	popad
	ret
;[cf]
;[cf]
;[of]:accessing
;[of]:get size
;[c]Returns the size of the data written in the buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]
;[c]RETURN VALUES
;[c]	eax	the size
;[c]
memory_buffer_get_size:

	mov	eax, [eax + memory_buffer.used]
	ret
;[cf]
;[of]:get start
;[c]Returns the base address of the buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]
;[c]RETURN VALUES
;[c]	eax	the buffer address
;[c]
memory_buffer_get_start:

	mov	eax, [eax + memory_buffer.p]
	ret
;[cf]
;[of]:get limit
;[c]Returns the limit of the buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]
;[c]RETURN VALUES
;[c]	eax	the limit of the buffer (last byte + 1)
;[c]
memory_buffer_get_limit:

	mov	ebx, [eax + memory_buffer.p]
	add	ebx, [eax + memory_buffer.used]
	mov	eax, ebx
	ret
;[cf]
;[of]:set
;[c]Copy the content of another buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the buffer
;[c]	ebx	the source buffer
;[c]
memory_buffer_set:

	push	esi
	push	edi
	mov	esi, eax
	mov	edi, ebx
	
	mov	ebx, [edi + memory_buffer.used]
	call	memory_buffer_reserve
	mov	ecx, [edi + memory_buffer.used]
	mov	[esi + memory_buffer.used], ecx
	
	mov	eax, [esi + memory_buffer.p]
	mov	ebx, [edi + memory_buffer.p]
	call	memory_copy

	pop	edi
	pop	esi
	ret
;[cf]
;[of]:set size
;[c]Change the size of the data written in the buffer
;[c]
;[c]	This method should be used to remove last bytes, not
;[c]	to increase the size.
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]	ebx	the size
;[c]
memory_buffer_set_size:

	mov	[eax + memory_buffer.used], ebx
	ret
;[cf]
;[cf]
;[of]:testing
;[of]:is empty
;[c]Returns true if the buffer is empty
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]
;[c]RETURN VALUES
;[c]	zf	1 if the buffer is empty
;[c]
;[c]REMARKS
;[c]	All registers are saved.
;[c]
memory_buffer_is_empty:

	cmp	[eax + memory_buffer.used], 0
	ret
;[cf]
;[cf]
;[of]:special
;[of]:make room
;[c]Make room at offset
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]	ebx	the offset
;[c]	ecx	the size in bytes
;[c]
;[c]RETURN VALUES
;[c]	eax	the address of the room
;[c]
memory_buffer_make_room:

	push	esi
	push	edi
	mov	esi, eax
	mov	edi, ebx

	; Reserve space
	push	ecx
	mov	ebx, ecx
	call	memory_buffer_reserve
	pop	edx

	; Move remaining 
	mov	ecx, [esi + memory_buffer.p]
	lea	ebx, [ecx + edi]
	lea 	eax, [ebx + edx]
	add	ecx, [esi + memory_buffer.used]
	sub	ecx, ebx
	push	ebx
	push	edx
	call	memory_move
	pop	edx
	pop	eax
	
	; Update size
	add	[esi + memory_buffer.used], edx

	pop	edi
	pop	esi
	ret
;[cf]
;[of]:get crc32
;[c]Returns the CRC32 of the memory buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]
;[c]RETURN VALUES
;[c]	eax	the CRC32
;[c]
memory_buffer_get_crc32:

	mov	ebx, [eax + memory_buffer.used]
	mov	eax, [eax + memory_buffer.p]
	jmp	get_crc32
;[cf]
;[of]:get md5
;[c]Returns the MD5 of the memory buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the memory buffer
;[c]	ebx	the MD5 buffer
;[c]
memory_buffer_get_md5:

	mov	ecx, ebx
	mov	ebx, [eax + memory_buffer.used]
	mov	eax, [eax + memory_buffer.p]
	jmp	get_md5
;[cf]
;[cf]

;[of]:private
;[of]:constants
DEFAULT_MEMORY_BUFFER_SIZE	=	256
;[cf]
;[cf]
;[cf]
;[of]:number
;[of]:definitions
size_of_dword	=	4
;[cf]
;[of]:min - max
min:	cmp	eax, ebx
	jge	.ge
	ret
.ge:	mov	eax, ebx
	ret

max:	cmp	eax, ebx
	jle	.le
	ret
.le:	mov	eax, ebx
	ret

;[cf]
;[cf]
;[of]:misc
;[of]:lookup
;[c]Lookup in a table for a value
;[c]
;[c]ARGUMENTS
;[c]	eax	the table (list of key+value pairs)
;[c]	ebx	the value
;[c]
;[c]RETURN VALUE
;[c]	eax	the value associated to the key or the last value (key = 0)
;[c]
lookup:

	sub	ecx, ecx
	sub	eax, 8	; avoid a jump
.next:	add	eax, 8
	cmp	ebx, [eax]
	jz	.found
	cmp	ecx, [eax]
	jnz	.next
.found:	mov	eax, [eax + 4]
	ret
;[cf]
;[of]:jump
;[c]Jump to an address associated to the matching value
;[c]
;[c]ARGUMENTS
;[c]	eax	the table (list of value+code)
;[c]	ebx	the value
;[c]
;[c]REMARKS
;[c]	The table has the following format
;[c]	
;[c]		dd	value1,	func1
;[c]		dd	value2,	func2
;[c]		...		
;[c]		dd	-1,	default-func
;[c]
;[c]	The default function is invoked if no value matches.
;[c]
jump:

	sub	ecx, ecx
	dec	ecx
	sub	eax, 8	; avoid a jump
.next:	add	eax, 8
	cmp	ebx, [eax]
	jz	.found
	cmp	ecx, [eax]
	jnz	.next
.found:	jmp	dword [eax + 4]
;[cf]
;[cf]
;[cf]
;[of]:text
;[of]:char
;[of]:definitions
size_of_wchar	=	2
size_of_char	=	1
;[cf]
;[of]:conversion
;[of]:lower
;[c]Convert a char to lower case
;[c]
;[c]ARGUMENTS
;[c]	al	char
;[c]
;[c]RETURN VALUES
;[c]	al	lower char
;[c]	
;[c]	All other registers saved.
;[c]
char_lower:
	cmp	ax, "A"
	jc	.done
	cmp	ax, "Z" + 1
	jnc	.done
	add	ax, "a" - "A"
.done:	ret
;[cf]
;[of]:upper
;[c]Convert a char to upper case
;[c]
;[c]ARGUMENTS
;[c]	ax	char
;[c]
;[c]RETURN VALUES
;[c]	ax	upper char
;[c]	
;[c]	All other registers saved.
;[c]
char_upper:
	cmp	ax, "a"
	jc	.done
	cmp	ax, "a" + 1
	jnc	.done
	add	ax, "A" - "a"
.done:	ret
;[cf]
;[of]:hex digit
;[c]Convert an hex digit char
;[c]
;[c]ARGUMENTS
;[c]	ax	the char
;[c]
;[c]RETURN VALUES
;[c]	zf	1 if hex digit
;[c]	al	the value if hexa
;[c]
char_hex_digit:

	cmp	ax, "0"
	jc	.no
	cmp	ax, "9" + 1
	jc	.deci
	cmp	ax, "A"
	jc	.no
	cmp	ax, "F" + 1
	jc	.hexa
	cmp	ax, "a"
	jc	.no
	cmp	ax, "f" + 1
	jnc	.no
	sub	ax, "a" - "A"
.hexa:	sub	ax, "A" - 10 - "0"	; trick: -"0" save a jump
.deci:	sub	ax, "0"

	test	al, 0
	ret

.no:	or	ax, ax
	ret
;[cf]
;[cf]
;[cf]
;[of]:string
;[of]:class

empty_string	db	0
;[cf]
;[of]:instance creation
;[of]:copy range
;[c]Create a string from another string
;[c]
;[c]ARGUMENTS
;[c]	eax	the string
;[c]	ebx	the size of the string
;[c]	
;[c]RETURN VALUES
;[c]	eax	the copy of the string
;[c]
string_copy_range:

	push	esi
	push	edi
	mov	esi, eax
	mov	eax, ebx
	jmp	string_copy.with_size
;[cf]
;[of]:copy
;[c]Create a string from another string
;[c]
;[c]ARGUMENTS
;[c]	eax	the string
;[c]	
;[c]RETURN VALUES
;[c]	eax	the copy of the string
;[c]
string_copy:

	; If the string is the empty string, just return it
	cmp	eax, empty_string
	jz	.done
	
	push	esi
	push	edi
	mov	esi, eax
	
	call	string_get_size
	
.with_size:	or	eax, eax
	jz	.empty
	
	push	eax
	
	inc	eax	; one more char (final zero)
	add	eax, 4	; 4 byte to store size
	mov	edi, eax	; save raw size
	call	get_memory	
	mov	[eax], edi	; store raw size
	add	eax, 4	; skip raw size
	
	pop	ecx
	mov	edi, eax
	rep	movsb
	
	mov	byte [edi], 0
	
	pop	edi
	pop	esi
.done:	ret

.empty:	mov	eax, empty_string
	pop	edi
	pop	esi
	ret
;[cf]
;[of]:delete
;[c]Delete a string
;[c]
;[c]ARGUMENTS
;[c]	eax	the string
;[c]	
;[c]REMARKS
;[c]	Deleting the empty string has no effect.
;[c]
string_delete:

	cmp	eax, empty_string
	jz	return
	jmp	free_memory_with_size
;[cf]
;[cf]
;[of]:accessing
;[of]:get size
;[c]Returns the length of the string
;[c]
;[c]ARGUMENTS
;[c]	eax	the string
;[c]
;[c]RETURN VALUES
;[c]	eax	the length of the string
;[c]	zf	1 if length is null
;[c]
string_get_size:

	push	esi

	mov	ebx, eax
	mov	esi, eax
.loop:	lodsb
	or	al, al
	jnz	.loop

	sub	esi, ebx
	lea	eax, [esi - 1]
	or	eax, eax

	pop	esi
	ret
;[cf]
;[of]:get hash
;[c]Returns the hash value of the string
;[c]
;[c]ARGUMENTS
;[c]	eax	the string
;[c]	
;[c]RETURN VALUE
;[c]	eax	the hash
;[c]
;[c]REMARKS
;[c]	this algorithm (k=33) was first reported by dan bernstein many years 
;[c]	ago in comp.lang.c. another version of this algorithm (now favored 
;[c]	by bernstein) uses xor: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic 
;[c]	of number 33 (why it works better than many other constants, prime 
;[c]	or not) has never been adequately explained.
;[c]
string_get_hash:

	push	esi

	mov	esi, eax
	mov	ebx, 1234
	jmp	.first
.loop:	mov	ecx, ebx	; * 33
	shl	ebx, 5	;
	add	ebx, ecx	;
	xor	ebx, eax
.first:	movzx	eax, byte [esi]
	add	esi, size_of_char
	or	eax,eax
	jnz	.loop
	
	mov	eax,ebx
	pop	esi
	ret
;[cf]
;[of]:compare
;[c]Compare two strings
;[c]
;[c]ARGUMENTS
;[c]	eax	string1
;[c]	ebx	string2
;[c]	
;[c]RETURN VALUES
;[c]	flags 	according to string1 - string2
;[c]
string_compare:

	sub	ecx, ecx
	jmp	.first

.next:	add	eax, 1
	add	ebx, 1
.first:	mov	cl, [eax]
	cmp	cl, [ebx]
	jnz	.done
	or	ecx, ecx
	jnz	.next

.done:	ret
;[cf]
;[cf]
;[cf]
;[of]:string buffer
;[of]:definitions
;[c]
;[c]	IMPORTANT: this structure must be the same as memory_buffer since
;[c]	the code is shared (such as the reserve function).
;[c]
	struct	string_buffer
	ptr	p,	byte
	attr	used,	dword		; used bytes
	attr	buffer_size,	dword		; size of buffer in bytes
	ends

DEFAULT_STRING_BUFFER_SIZE	=	4096
;[cf]
;[of]:initialize - release
;[of]:initialize
;[c]Initialize a string buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]
string_buffer_initialize:

	push	esi
	mov	esi, eax
	
	mov	eax, DEFAULT_STRING_BUFFER_SIZE * size_of_char
	call	get_memory
	mov	[esi + string_buffer.p], eax
	sub	ebx, ebx
	mov	[esi + string_buffer.used], ebx
	mov	[esi + string_buffer.buffer_size], DEFAULT_STRING_BUFFER_SIZE * size_of_char

	pop	esi
	ret
;[cf]
;[of]:release
;[c]Release the string buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]
string_buffer_release	=	memory_buffer_release
;[cf]
;[cf]
;[of]:accessing
;[of]:get size
;[c]Returns the number of chars written in the buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]
;[c]RETURN VALUES
;[c]	eax	the size
;[c]
string_buffer_get_size:

	mov	eax, [eax + string_buffer.used]
	;shr	eax, 0	; divide by size_of_char
	ret
;[cf]
;[of]:get chars
;[c]Returns the string buffer chars
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]
;[c]RETURN VALUES
;[c]	eax	the chars
;[c]
string_buffer_get_chars:

	mov	eax, [eax + string_buffer.p]
	ret
;[cf]
;[of]:get chars and size
;[c]Returns the string buffer's chars and its size
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]
;[c]RETURN VALUES
;[c]	eax	the chars
;[c]	ebx	the size
;[c]
string_buffer_get_chars_and_size:

	mov	ebx, [eax + string_buffer.used]
	;shr	ebx, 0	; divide by size_of_char
	mov	eax, [eax + string_buffer.p]
	ret
;[cf]
;[cf]
;[of]:converting
;[of]:as string
;[c]Returns the string buffer as string
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]
;[c]RETURN VALUES
;[c]	eax	the string
;[c]
string_buffer_as_string:

	sub	ecx, ecx
	mov	ebx, [eax + string_buffer.p]
	add	ebx, [eax + string_buffer.used]
	mov	[ebx], cx
	mov	eax, [eax + string_buffer.p]
	ret
;[cf]
;[of]:new string
;[c]Returns a new string from the string buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]
;[c]RETURN VALUES
;[c]	eax	the string
;[c]
string_buffer_new_string:

	call	string_buffer_as_string
	jmp	string_copy
;[cf]
;[cf]
;[of]:adding - removing
;[of]:add string
;[c]Append a string to the string buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]	ebx	the string
;[c]
string_buffer_add_string:

	push	esi
	push	edi
	mov	esi, eax
	mov	edi, ebx
	
	mov	eax, ebx
	call	string_get_size
	
.common:	; shared with add-range
	
	;shl	eax, 0	; multiply by size_of_char
	push	eax
	mov	ebx, eax
	mov	eax, esi
	call	string_buffer_reserve
	
	mov	eax, [esi + string_buffer.p]
	add	eax, [esi + string_buffer.used]
	mov	ebx, edi
	pop	ecx
	add	[esi + string_buffer.used], ecx
	call	memory_copy
	
	or	esi, esi
	pop	edi
	pop	esi
	ret
;[cf]
;[of]:add range
;[c]Append a range to the string buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]	ebx	the string
;[c]	ecx	the size
;[c]
string_buffer_add_range:

	push	esi
	push	edi
	mov	esi, eax
	mov	edi, ebx
	mov	eax, ecx
	jmp	string_buffer_add_string.common
;[cf]
;[of]:add char
;[c]Append a string to the string buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]	bl	the char
;[c]
string_buffer_add_char:

	push	esi
	mov	esi, eax

	push	ebx
	mov	ebx, size_of_char
	call	string_buffer_reserve
	pop	ebx
	
	mov	eax, [esi + string_buffer.p]
	add	eax, [esi + string_buffer.used]
	mov	[eax], bl

	add	[esi + string_buffer.used], size_of_char
	
	pop	esi
	ret
;[cf]
;[of]:allocate
;[c]Allocate a range of characters
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]	ebx	the number of chars to reserve
;[c]	
;[c]RETURN VALUES
;[c]	eax	the address of the allocated space
;[c]
string_buffer_allocate:

	push	esi
	mov	esi, eax

	shl	ebx, 1
	push	ebx
	call	string_buffer_reserve
	pop	ebx
	
	mov	eax, [esi + string_buffer.p]
	add	eax, [esi + string_buffer.used]
	add	[esi + string_buffer.used], ebx
	
	pop	esi
	ret
;[cf]

;[of]:remove all
;[c]Empty the content of the buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]
string_buffer_remove_all	=	memory_buffer_remove_all
;[cf]
;[of]:remove
;[c]Removes a range
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]	ebx	the offset in chars
;[c]	ecx	number of chars to remove
;[c]
string_buffer_remove:

	;shl	ebx, 0	; multiply by size_of_char
	;shl	ecx, 0	; multiply by size_of_char
	jmp	memory_buffer_remove
;[cf]
;[cf]

;[of]:private
;[of]:reserve
;[c]Reserve some chars
;[c]
;[c]ARGUMENTS
;[c]	eax	the string buffer
;[c]	ebx	the size to reserve in bytes
;[c]
string_buffer_reserve:

	add	ebx, size_of_char	; we always must have one more char
	
	jmp	memory_buffer_reserve
;[cf]
;[cf]
;[cf]
;[of]:command line
;[of]:definitions
	struct	command_line
	attr	p,	dword
	attr	current_argument,	string_buffer
	ends
;[cf]
;[of]:initialize - release
;[of]:initialize
;[c]Initializes the command line object
;[c]
;[c]ARGUMENTS
;[c]	eax	the command line
;[c]
command_line_initialize:

	push	esi
	mov	esi, eax

	; Get the beginning of the command line
	call	[GetCommandLineA]
	mov	[esi + command_line.p], eax
	
	; Initialize the buffer to store arguments
	lea	eax, [esi + command_line.current_argument]
	call	string_buffer_initialize
	
	; Skip the first argument (the name of the program)
	mov	eax, esi
	call	command_line_get_next
	
	pop	esi
	ret

	
;[cf]
;[of]:release
;[c]Release the command line object
;[c]
;[c]ARGUMENTS
;[c]	eax	the command line object
;[c]
command_line_release:

	lea	eax, [eax + command_line.current_argument]
	jmp	string_buffer_release
;[cf]
;[cf]
;[of]:reading
;[of]:get next
;[c]Returns the next argument if any
;[c]
;[c]ARGUMENTS
;[c]	eax	the command line
;[c]
;[c]RETURN VALUES
;[c]	eax	the argument
;[c]	zf	1 if no more argument
;[c]
command_line_get_next:

	push	esi
	push	edi
	mov	edi, eax
	mov	esi, [edi + command_line.p]
	
	; Reset the buffer
	lea	eax, [edi + command_line.current_argument]
	call	string_buffer_remove_all
	
	call	.skip_blanks
	
	or	al, al
	jz	.eol
	cmp	al, """"
	jz	.read_quoted
	
	; Read unquoted argument
.u:	mov	al, [esi]
	or	al, al
	jz	.done
	cmp	al, 32
	jz	.done
	cmp	al, 9
	jz	.done
	add	esi, size_of_char
	call	.add_char
	jmp	.u
	 
.read_quoted:	; Read quoted argument
	mov	cl, [esi]
	add	esi, size_of_char
.q:	mov	al, [esi]
	or	al, al
	jz	.done	; unexpected end of line
	add	esi, size_of_char
	cmp	al, cl
	jz	.done
	push	ecx
	call	.add_char
	pop	ecx
	jmp	.q
	
	; Save the pointer
.done:	mov	[edi + command_line.p], esi
	
	; Return the argument
	lea	eax, [edi + command_line.current_argument]
	call	string_buffer_as_string
	or	eax, eax	; force zf = 0
	
.eol:	pop	edi
	pop	esi
	ret
;[c]	
.add_char:

	mov	bl, al
	lea	eax, [edi + command_line.current_argument]
	jmp	string_buffer_add_char
;[c]	
.skip_blanks:

	lodsb
	cmp	al, 32
	jz	.skip_blanks
	cmp	al, 9
	jz	.skip_blanks
	sub	esi, size_of_char
	ret
;[cf]
;[cf]
;[cf]
;[cf]
;[of]:collection
;[of]:vector
;[of]:definitions
;[c]	
;[c]	IMPORTANT: this structure must be the same as memory_buffer since
;[c]	the code is shared (such as the reserve function).
;[c]	
	struct	vector
	ptr	p,	dword
	attr	used,	dword	; used bytes
	attr	buffer_size,	dword	; size of buffer in bytes
	ends
;[cf]
;[of]:initialize - release
;[of]:initialize
;[c]Initialize a vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]
vector_initialize	=	memory_buffer_initialize
;[cf]
;[of]:release
;[c]Release the vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]
vector_release	=	memory_buffer_release
;[cf]
;[of]:copy
;[c]Copy a vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the source vector
;[c]
vector_copy	=	memory_buffer_copy
;[cf]
;[cf]
;[of]:adding - removing
;[of]:add
;[c]Append a value to the vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the value
;[c]
vector_add:

	push	eax
	push	ebx
	mov	ebx, size_of_dword
	call	vector_reserve
	pop	ebx
	pop	eax
	
	mov	ecx, [eax + vector.p]
	add	ecx, [eax + vector.used]
	mov	[ecx], ebx
	add	[eax + vector.used], size_of_dword
	ret
;[cf]
;[of]:add all
;[c]Add all elements from another vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the source vector
;[c]
vector_add_all:

	mov	ecx, [ebx + vector.used]
	mov	ebx, [ebx + vector.p]
	jmp	memory_buffer_add_bytes
;[cf]
;[of]:insert
;[c]Insert a value at index
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the index
;[c]	ecx	the value
;[c]
vector_insert:

	push	ecx
	shl	ebx, 2	; * size_of_dword
	mov	ecx, size_of_dword
	call	memory_buffer_make_room
	pop	ecx
	mov	[eax], ecx
	ret
;[cf]
;[of]:insert all
;[c]Insert all values from a vector at index
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the index
;[c]	ecx	the vector
;[c]
vector_insert_all:

	; Make room
	push	ecx
	shl	ebx, 2	; * size_of_dword
	mov	ecx, [ecx + vector.used]
	call	memory_buffer_make_room
	pop	ecx

	; Copy data
	mov	ebx, [ecx + vector.p]
	mov	ecx, [ecx + vector.used]
	jmp	memory_copy
;[cf]

;[of]:remove
;[c]Remove a value at index
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the index
;[c]
vector_remove:

	shl	ebx, 2	; * size_of_dword
	mov	ecx, size_of_dword
	jmp	memory_buffer_remove
;[cf]
;[of]:remove range
;[c]Remove a range of value
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the first index
;[c]	ecx	the number of items to remove
;[c]
vector_remove_range:

	shl	ebx, 2	; * size_of_dword
	shl	ecx, 2	; * size_of_dword
	jmp	memory_buffer_remove
;[cf]
;[of]:remove value
;[c]Remove a value
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the value
;[c]
;[c]REMARKS
;[c]	The method has no effect if the value is not found.
;[c]
vector_remove_value:

	push	esi
	mov	ecx, .scan
	mov	esi, -1
	push	eax
	call	vector_until_z
	pop	eax
	jnz	.done
	mov	ebx, esi
	call	vector_remove
.done:	pop	esi
	ret

.scan:	inc	esi
	cmp	ebx, eax
	ret
;[cf]
;[of]:remove all
;[c]Empty the content of the vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]
vector_remove_all	=	memory_buffer_remove_all
;[cf]
;[of]:remove last
;[c]Remove the last value
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]
vector_remove_last:

	sub	[eax + vector.used], size_of_dword
	ret
;[cf]
;[cf]
;[of]:accessing
;[of]:get size
;[c]Return the number of elements
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	
;[c]RETURN VALUES
;[c]	eax	the number of elements
;[c]	zf	1 if the vector is empty
;[c]
vector_get_size:

	mov	eax, [eax + vector.used]
	shr	eax, 2	; divide by size_of_dword
	ret
;[cf]
;[of]:set size
;[c]Change the number of elements
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the number of elements
;[c]	
;[c]REMARKS
;[c]	This method must only be used to remove 0, 1 or more terminal elements.
;[c]
vector_set_size:

	shl	ebx, 2	; multiply by size_of_dword
	mov	[eax + vector.used], ebx
	ret
;[cf]
;[of]:get value
;[c]Return an element at index
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the index
;[c]	
;[c]RETURN VALUES
;[c]	eax	the element
;[c]
vector_get_value:

	mov	eax, [eax + vector.p]
	mov	eax, [eax + ebx * size_of_dword]
	ret
;[cf]
;[of]:set value
;[c]Change the value at index
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the index
;[c]	ecx	the value
;[c]	
vector_set_value:

	mov	eax, [eax + vector.p]
	mov	[eax + ebx * size_of_dword], ecx
	ret
;[cf]
;[of]:set
;[c]Copy the content from another vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the source vector
;[c]
vector_set	=	memory_buffer_set
;[cf]
;[of]:get first
;[c]Return the first element
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	
;[c]RETURN VALUES
;[c]	eax	the element
;[c]
vector_get_first:

	mov	eax, [eax + vector.p]
	mov	eax, [eax]
	ret
;[cf]
;[of]:get last
;[c]Return the last element
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	
;[c]RETURN VALUES
;[c]	eax	the element
;[c]
vector_get_last:

	mov	ebx, [eax + vector.p]
	add	ebx, [eax + vector.used]
	mov	eax, [ebx - size_of_dword]
	ret
;[cf]
;[cf]
;[of]:enumerating
;[of]:each
;[c]Enumerates all elements of the vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	user argument
;[c]	ecx	the function to call for each element
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]RETURN VALUES
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]REMARKS
;[c]	User arguments are passed to the function and can be modified.
;[c]
vector_each:
	mov	edx, ecx

	mov	ecx, [eax + vector.used]
	shr	ecx, 2	; divide by size_of_dword
	mov	eax, [eax + vector.p]
	jecxz	.done
	
.loop:	push	eax
	push	ebx
	push	ecx
	push	edx
	mov	eax, [eax]
	call	edx
	pop	edx
	pop	ecx
	pop	ebx
	pop	eax
	add	eax, size_of_dword
	loop	.loop

.done:	ret
;[cf]
;[of]:until z
;[c]Enumerates all elements of the vector until zf=1
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	user argument
;[c]	ecx	the function to call for each element
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]RETURN VALUES
;[c]	eax	the last processed item (if zf=1)
;[c]	zf	1 if interrupted
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]REMARKS
;[c]	User arguments are passed to the function and can be modified.
;[c]
vector_until_z:
	mov	edx, ecx

	mov	ecx, [eax + vector.used]
	shr	ecx, 2	; divide by size_of_dword
	mov	eax, [eax + vector.p]
	jecxz	.done
	
.loop:	push	eax
	push	ebx
	push	ecx
	push	edx
	mov	eax, [eax]
	call	edx
	pop	edx
	pop	ecx
	pop	ebx
	pop	eax
	jz	.break
	add	eax, size_of_dword
	loop	.loop

.done:	sub	al, al		; force zf = 0
	inc	al		;
	ret

.break:	mov	eax, [eax]	; return the last item
	ret
;[cf]
;[of]:each and release
;[c]Enumerates all elements of the vector and release the vector
;[c]
;[c]	This method is useful to delete elements and release the vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	user argument
;[c]	ecx	the function to call for each element
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]RETURN VALUES
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]REMARKS
;[c]	User arguments are passed to the function and can be modified.
;[c]
vector_each_and_release:

	push	eax
	call	vector_each
	pop	eax
	jmp	vector_release
;[cf]
;[of]:each with index
;[c]Enumerates all elements of the vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	user argument
;[c]	ecx	the function to call for each element
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]RETURN VALUES
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]REMARKS
;[c]	User arguments are passed to the function and can be modified.
;[c]
vector_each_with_index:

	mov	edx, ecx
	mov	ecx, [eax + vector.used]
	shr	ecx, 2	; divide by size_of_dword
	push	ecx
	mov	eax, [eax + vector.p]
	sub	ecx, ecx
	jmp	.first
	
.loop:	push	eax
	push	ebx
	push	ecx
	push	edx
	mov	eax, [eax]
	call	edx
	pop	edx
	pop	ecx
	pop	ebx
	pop	eax
	add	eax, size_of_dword
	inc	ecx
.first:	cmp	ecx, [esp]
	jnz	.loop

	pop	ecx
	ret
;[cf]
;[cf]
;[of]:testing
;[of]:is empty
;[c]Returns true if the vector is empty
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]
;[c]RETURN VALUES
;[c]	zf	1 if empty
;[c]
vector_is_empty	=	vector_get_size
;[cf]
;[cf]

;[of]:private
;[of]:reserve
;[c]Reserve some chars
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the size to reserve in bytes
;[c]
vector_reserve	=	memory_buffer_reserve
;[cf]
;[cf]
;[cf]
;[of]:sorted vector
;[of]:[description]
;[c]A sorted vector is a vector where elements are sorted using a compare function.
;[c]
;[cf]
;[of]:definitions
	struct	sorted_vector, vector
	attr	sort_function,	dword
	ends
;[cf]
;[of]:initialize - release
;[of]:initialize
;[c]Initialize a sorted vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the compare function
;[c]
sorted_vector_initialize:
	
	mov	[eax + sorted_vector.sort_function], ebx
	jmp	vector_initialize
;[cf]
;[of]:release
;[c]Release the sorted vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]
sorted_vector_release	=	vector_release
;[cf]
;[of]:copy
;[c]Copy a sorted vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the source vector
;[c]
sorted_vector_copy	=	vector_copy
;[cf]
;[cf]
;[of]:adding - removing
;[of]:add
;[c]Add a value to the sorted vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the value
;[c]
sorted_vector_add:

	push	esi
	push	edi
	mov	esi, eax
	
	sub	ecx, ecx	; left
	mov	edx, [eax + vector.used]	; right
	shr	edx, 2	; divide by size_of_dword
	dec	edx	;
	sub	edi, edi	; mid
	
	jmp	.first
.loop:	
	; mid = (left + right) / 2
	mov	edi, ecx
	add	edi, edx
	sar	edi, 1
	
	; Compare middle value with the value
	push	ebx
	push	ecx
	push	edx
	mov	eax, [esi + vector.p]
	mov	eax, [eax + edi * size_of_dword]
	call	[esi + sorted_vector.sort_function]
	pop	edx
	pop	ecx
	pop	ebx
	
	jnc	.left	; value before mid
	
	; Value after mid
	lea	ecx, [edi + 1]	; left = mid + 1
	inc	edi
	jmp	.next
	
.left:	; Value before mid
	lea	edx, [edi - 1]	; right = mid - 1

.next:
.first:	cmp	edx, ecx
	jge	.loop
	
	mov	eax, esi
	mov	ecx, ebx
	mov	ebx, edi
	call	vector_insert
	
	pop	edi
	pop	esi
	ret
;[cf]

;[of]:remove all
;[c]Empty the content of the vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]
sorted_vector_remove_all	=	vector_remove_all
;[cf]
;[cf]
;[of]:searching
;[of]:get index
;[c]Returns the index of a value
;[c]
;[c]ARGUMENTS
;[c]	eax	the sorted vector
;[c]	ebx	the value
;[c]
;[c]RETURN VALUES
;[c]	eax	the index
;[c]	zf	1 if found
;[c]
sorted_vector_get_index:

	push	esi
	push	edi
	mov	esi, eax

	sub	ecx, ecx	; left
	mov	edx, [eax + vector.used]	; right
	shr	edx, 2	; divide by size_of_dword
	dec	edx	;
	sub	edi, edi	; mid
	
	jmp	.next
.loop:	
	; mid = (left + right) / 2
	mov	edi, ecx
	add	edi, edx
	sar	edi, 1
	
	; Compare middle value with the value
	push	ebx
	push	ecx
	push	edx
	mov	eax, [esi + vector.p]
	mov	eax, [eax + edi * size_of_dword]
	call	[esi + sorted_vector.sort_function]
	pop	edx
	pop	ecx
	pop	ebx
	jz	.found
	jnc	.left
	
	; Value after mid
	lea	ecx, [edi + 1]	; left = mid + 1
	jmp	.next
	
.left:	; Value before mid
	lea	edx, [edi - 1]	; right = mid - 1

.next:	cmp	edx, ecx
	jge	.loop	; zf = 0 when loop exits

.found:	mov	eax, edi
	pop	edi
	pop	esi
	ret
;[cf]
;[cf]
;[of]:accessing
;[of]:get size
;[c]Return the number of elements
;[c]
;[c]ARGUMENTS
;[c]	eax	the sorted vector
;[c]	
;[c]RETURN VALUES
;[c]	eax	the number of elements
;[c]	zf	1 if the vector is empty
;[c]
sorted_vector_get_size = vector_get_size
;[cf]
;[of]:get value
;[c]Return an element at index
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the index
;[c]	
;[c]RETURN VALUES
;[c]	eax	the element
;[c]
sorted_vector_get_value	=	vector_get_value
;[cf]
;[of]:set
;[c]Copy the content from another sorted vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	the source vector
;[c]
sorted_vector_set	=	vector_set
;[cf]
;[of]:get first
;[c]Return the first element
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	
;[c]RETURN VALUES
;[c]	eax	the element
;[c]
sorted_vector_get_first	= vector_get_first
;[cf]
;[of]:get last
;[c]Return the last element
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	
;[c]RETURN VALUES
;[c]	eax	the element
;[c]
sorted_vector_get_last	= vector_get_last
;[cf]
;[cf]
;[of]:enumerating
;[of]:each
;[c]Enumerates all elements of the vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	user argument
;[c]	ecx	the function to call for each element
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]RETURN VALUES
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]REMARKS
;[c]	User arguments are passed to the function and can be modified.
;[c]
sorted_vector_each	= vector_each
;[cf]
;[of]:until z
;[c]Enumerates all elements of the vector until zf=1
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	user argument
;[c]	ecx	the function to call for each element
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]RETURN VALUES
;[c]	eax	the last processed item (if zf=1)
;[c]	zf	1 if interrupted
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]REMARKS
;[c]	User arguments are passed to the function and can be modified.
;[c]
sorted_vector_until_z = vector_until_z
;[cf]
;[of]:each and release
;[c]Enumerates all elements of the vector and release the vector
;[c]
;[c]	This method is useful to delete elements and release the vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	user argument
;[c]	ecx	the function to call for each element
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]RETURN VALUES
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]REMARKS
;[c]	User arguments are passed to the function and can be modified.
;[c]
sorted_vector_each_and_release	= vector_each_and_release
;[cf]
;[of]:each with index
;[c]Enumerates all elements of the vector
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]	ebx	user argument
;[c]	ecx	the function to call for each element
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]RETURN VALUES
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]REMARKS
;[c]	User arguments are passed to the function and can be modified.
;[c]
sorted_vector_each_with_index	=	vector_each_with_index
;[cf]
;[cf]
;[of]:testing
;[of]:is empty
;[c]Returns true if the vector is empty
;[c]
;[c]ARGUMENTS
;[c]	eax	the vector
;[c]
;[c]RETURN VALUES
;[c]	zf	1 if empty
;[c]
sorted_vector_is_empty	=	vector_is_empty
;[cf]
;[cf]
;[cf]
;[of]:dictionary
;[of]:definitions
	struct	dictionary
	ptr	class,	dictionary_class
	attr	associations,	dword
	attr	size,	dword
	attr	tally,	dword
	attr	collisions,	dword
	ends

	struct	dictionary_class
	attr	hash,	dword	; the hash function
	attr	equals,	dword	; the compare function
	ends
;[cf]
;[of]:initialize - release
;[of]:initialize
;[c]Initialize a dictionary
;[c]
;[c]ARGUMENTS
;[c]	eax	the dictionary
;[c]	ebx	the dicionary class
;[c]
dictionary_initialize:

	push	esi
	mov	esi, eax
	
	sub	eax, eax
	mov	[esi + dictionary.class], ebx
	mov	[esi + dictionary.collisions], eax
	mov	[esi + dictionary.tally], eax
	
	mov	eax, DEFAULT_DICTIONARY_SIZE
	mov	[esi + dictionary.size], eax
	call	create_associations
	mov	[esi + dictionary.associations], eax
	
	pop	esi
	ret

DEFAULT_DICTIONARY_SIZE	=	8192 + 7
;[cf]
;[of]:release
;[c]Release a dictionary
;[c]
;[c]ARGUMENTS
;[c]	eax	the dictionary
;[c]
dictionary_release:

	mov	ebx, [eax + dictionary.size]
	mov	eax, [eax + dictionary.associations]
	jmp	delete_associations
;[cf]
;[cf]
;[of]:adding - removing
;[of]:add
;[c]Add a value
;[c]
;[c]ARGUMENTS
;[c]	eax	the dictionary
;[c]	ebx	the key
;[c]	ecx	the value
;[c]
;[c]RETURN VALUES
;[c]	eax	the previous value with same key or null
;[c]	zf	1 if no previous value
;[c]
dictionary_add:

	push	esi
	push	edi
	push	ebp
	mov	esi, eax
	
	locals
	local	.key,	dword
	local	.value,	dword
	local	.collisions,	dword
	endl
	
	mov	[.key], ebx
	mov	[.value], ecx
	
	mov	eax, ebx
	call	dictionary_slot
	mov	ecx, eax
	mov	edi, [esi + dictionary.associations]
	mov	ebx, [.key]	; the key
	mov	[.collisions], 1	; collisions
.1:	mov	eax, [edi + ecx * sizeof.association + association.key]
	or	eax, eax
	jz	.create
	call	dictionary_compare
	jz	.found
	inc	[.collisions]
	inc	ecx
	cmp	ecx, [esi + dictionary.size]
	jnz	.1
	sub	ecx, ecx
	jmp	.1
	
;[c]	
;[c]	Update the assocation
;[c]	
;[c]	Replace the value of the existing association
;[c]	
.found:	; retrieve old value
	mov	eax, [edi + ecx * sizeof.association + association.value]
	
	; store new value
	mov	ebx, [.value]
	mov	[edi + ecx * sizeof.association + association.value], ebx
	
	or	eax, eax
	jmp	.done
	
;[c]	
;[c]	Create a new association
;[c]
;[c]	There is no association with the same key, so we create a new 
;[c]	association.
;[c]	
.create:	mov	eax, [.key]
	mov	ebx, [.value]
	mov	[edi + ecx * sizeof.association + association.key], eax
	mov	[edi + ecx * sizeof.association + association.value], ebx
	
	mov	eax, [.collisions]
	call	dictionary_upate_collisions
	call	dictionary_increase_tally
	
	; no previous value: return null
	sub	eax, eax
	
;[c]	
.done:	mov	esp, ebp
	pop	ebp
	pop	edi
	pop	esi
	ret
;[cf]
;[cf]
;[of]:searching
;[of]:get value
;[c]Returns the value associated to a key
;[c]
;[c]ARGUMENTS
;[c]	eax	the dictionary
;[c]	ebx	the key
;[c]
;[c]RETURN VALUES
;[c]	eax	the value
;[c]	zf	1 if not found
;[c]	
dictionary_get_value:

	push	esi
	push	edi
	push	ebp
	mov	esi, eax
	
	mov	eax, ebx
	call	dictionary_slot
	mov	ecx, eax
	mov	edi, [esi + dictionary.associations]
	mov	ebp, [esi + dictionary.collisions]
	jmp	.3
.1:	call	dictionary_compare
	jz	.found
	inc	ecx
	cmp	ecx, [esi + dictionary.size]
	jz	.end_of_array
.2:	dec	ebp
.3:	mov	eax, [edi + ecx * sizeof.association + association.key]
	or	eax, eax
	jz	.done
	or	ebp, ebp
	jnz	.1
	sub	eax, eax
	jmp	.done
	
.found:	; retrieve value
	mov	eax, [edi + ecx * sizeof.association + association.value]
	or	eax, eax
	
.done:	pop	ebp
	pop	edi
	pop	esi
	ret

;[c]
.end_of_array:

	sub	ecx, ecx
	jmp	.2
;[cf]
;[cf]
;[of]:enumerating
;[of]:each value
;[c]Enumerate all values
;[c]
;[c]ARGUMENTS
;[c]	eax	the dictionary
;[c]	ebx	user data
;[c]	ecx	the function
;[c]	esi	user data
;[c]	edi	user data
;[c]	ebp	user data
;[c]
;[c]RETURN VALUES
;[c]	esi, edi, ebp
;[c]
;[c]REMARKS
;[c]	* eax is the value passed to the function
;[c]	* ebx is passed to the function
;[c]	* esi, edi, ebp are passed to the function and can be modified
;[c]
dictionary_each_value:

	mov	edx, ecx

	mov	ecx, [eax + dictionary.size]
	mov	eax, [eax + dictionary.associations]

.loop:	cmp	[eax + association.key], 0
	jz	.next
	push	eax
	push	ebx
	push	ecx
	push	edx
	mov	eax, [eax + association.value]
	call	edx
	pop	edx
	pop	ecx
	pop	ebx
	pop	eax
.next:	add	eax, sizeof.association
	loop	.loop

	ret
;[cf]
;[of]:each value and release
;[c]Enumerate all values and release.
;[c]
;[c]ARGUMENTS
;[c]	eax	the dictionary
;[c]	ebx	user data
;[c]	ecx	the function
;[c]	esi	user data
;[c]	edi	user data
;[c]	ebp	user data
;[c]
;[c]RETURN VALUES
;[c]	esi, edi, ebp
;[c]
;[c]REMARKS
;[c]	This method works like each-value but it releases the dictionary
;[c]	immediately after enumerating values.
;[c]	
;[c]	This method is useful to delete all items of a dictionary and 
;[c]	release it.
;[c]
dictionary_each_value_and_release:

	push	eax
	call	dictionary_each_value
	pop	eax
	jmp	dictionary_release
;[cf]
;[of]:until z
;[c]Enumerate all values until zf=1
;[c]
;[c]ARGUMENTS
;[c]	eax	the dictionary
;[c]	ebx	user data
;[c]	ecx	the function
;[c]	esi	user data
;[c]	edi	user data
;[c]	ebp	user data
;[c]
;[c]RETURN VALUES
;[c]	eax	the last processed item (if zf = 1)
;[c]	zf	1 if interrupted
;[c]	esi, edi, ebp	
;[c]
;[c]REMARKS
;[c]	* eax is the value passed to the function
;[c]	* ebx is passed to the function
;[c]	* esi, edi, ebp are passed to the function and can be modified
;[c]
dictionary_until_z:

	mov	edx, ecx

	mov	ecx, [eax + dictionary.size]
	mov	eax, [eax + dictionary.associations]

.loop:	cmp	[eax + association.key], 0
	jz	.next
	push	eax
	push	ebx
	push	ecx
	push	edx
	mov	eax, [eax + association.value]
	call	edx
	pop	edx
	pop	ecx
	pop	ebx
	pop	eax
	jz	.break
.next:	add	eax, sizeof.association
	loop	.loop

	sub	al, al		; force zf = 0
	inc	al		;
	ret

.break:	mov	eax, [eax + association.value]	; return the last item
	ret
;[cf]
;[cf]
;[of]:private
;[of]:structures
	struct	association
	attr	key,	dword
	attr	value,	dword
	ends
;[cf]

;[of]:increase tally
;[c]Increase the number of used slots
;[c]
;[c]ARGUMENTS
;[c]	esi	the dictionary
;[c]
private dictionary_increase_tally

	; Increment value
	mov	eax, [esi + dictionary.tally]
	inc	eax
	mov	[esi + dictionary.tally], eax

	; Ensure to not exceeds 75% limit
	lea	ebx, [eax * 4]
	mov	eax, [esi + dictionary.size]
	mov	ecx, 3
	mul	ecx
	cmp	ebx, eax
	jc	.ok
	call	dictionary_resize

.ok:	ret
;[cf]
;[of]:resize
;[c]Resize the dictionary by 150%
;[c]
;[c]ARGUMENTS
;[c]	esi	the dictionary
;[c]
private dictionary_resize

	pushad

	locals
	local	.new_size,	dword
	local	.new_array,	dword
	local	.new_collisions,	dword
	endl

	; eax = size * 3 / 2
	mov	eax, [esi + dictionary.size]
	lea	eax, [eax * 2 + eax]
	shr	eax, 1

	mov	[.new_size], eax
	call	create_associations
	mov	[.new_array], eax
	sub	ebx, ebx
	mov	[.new_collisions], ebx

	mov	ecx, [esi + dictionary.size]
	mov	edx, [esi + dictionary.associations]
.1:	push	ecx
	push	edx

	; Skip if the slot is empty
	mov	eax, [edx + association.key]
	or	eax, eax
	jz	.next

	; Copy the association
	call	.copy	

.next:	pop	edx
	pop	ecx
	add	edx, sizeof.association
	loop	.1

	; Delete the old array
	mov	eax, [esi + dictionary.associations]
	mov	ebx, [esi + dictionary.size]
	call	delete_associations
	
	; Replace with the new array
	mov	eax, [.new_size]
	mov	ebx, [.new_array]
	mov	ecx, [.new_collisions]
	mov	[esi + dictionary.size], eax
	mov	[esi + dictionary.associations], ebx
	mov	[esi + dictionary.collisions], ecx

	mov	esp, ebp
	popad
	ret

;[c]
.copy:
	; Get hash(key)
	mov	ebx, [esi + dictionary.class]
	call	[ebx + dictionary_class.hash]

	; Modulo size
	push	edx
	sub	edx, edx
	div	[.new_size]
	mov	eax, edx
	pop	edx

	; Search the first free slot
	mov	ecx, [.new_array]
	mov	ebx, 1	; collisions
.2:	cmp	[ecx + eax * sizeof.association + association.key], 0
	jz	.4
	inc	eax
	cmp	eax, [.new_size]
	jnz	.3
	sub	eax, eax
.3:	inc	ebx
	jmp	.2

	; save address
.4:	lea	ecx, [ecx + eax * sizeof.association]

	; Update collision counter
	mov	eax, [.new_collisions]
	call	max
	mov	[.new_collisions], eax

	; Copy association
	mov	eax, [edx + association.key]
	mov	ebx, [edx + association.value]
	mov	[ecx + association.key], eax
	mov	[ecx + association.value], ebx
	ret
;[cf]
;[of]:slot
;[c]Returns the slot of a key
;[c]
;[c]ARGUMENTS
;[c]	esi	the dictionary
;[c]	eax	the key
;[c]
;[c]RETURN VALUES
;[c]	eax	the slot
;[c]
;[c]REMARKS
;[c]	ebx saved.
;[c]
private dictionary_slot

	push	ebx

	; Get hash(key)
	mov	ebx, [esi + dictionary.class]
	call	[ebx + dictionary_class.hash]

	; Modulo size
	sub	edx, edx
	div	[esi + dictionary.size]
	mov	eax, edx

	pop	ebx
	ret
;[cf]
;[of]:update collisions
;[c]Update the collision count
;[c]
;[c]ARGUMENTS
;[c]	esi	the dictionary
;[c]	eax	the new counter
;[c]
private dictionary_upate_collisions

	mov	ebx, [esi + dictionary.collisions]
	call	max
	mov	[esi + dictionary.collisions], eax
	ret
;[cf]
;[of]:compare
;[c]Returns true if two keys are equals
;[c]
;[c]ARGUMENTS
;[c]	esi	the dictionary
;[c]	eax	the first key
;[c]	ebx	the second key
;[c]
;[c]RETURN VALUES
;[c]	zf	1 if key are equals
;[c]
;[c]REMARKS
;[c]	ebx, ecx saved.
;[c]
private dictionary_compare

	push	ebx
	push	ecx
	mov	ecx, [esi + dictionary.class]
	call	[ecx + dictionary_class.equals]
	pop	ecx
	pop	ebx
	ret
;[cf]

;[of]:create associations
;[c]Create an array of associations
;[c]
;[c]ARGUMENTS
;[c]	eax	the number of associations
;[c]
;[c]RETURN VALUES
;[c]	eax	the array
;[c]
private create_associations

	push	edi
	
	mov	ebx, sizeof.association
	mul	ebx
	push	eax
	call	get_memory
	pop	ebx

	push	eax	
	mov	edi, eax
	mov	ecx, ebx
	shr	ecx, 2
	sub	eax, eax
	rep stosd
	pop	eax
	
	pop	edi
	ret
;[cf]
;[of]:delete associations
;[c]
;[c]ARGUMENTS
;[c]	eax	the base address
;[c]	ebx	the number of associations
;[c]
private delete_associations

	xchg	eax, ebx
	mov	ecx, sizeof.association
	mul	ecx
	xchg	eax, ebx
	call	free_memory
	ret
;[cf]
;[cf]
;[cf]
;[of]:identity dictionary
;[of]:definitions
	struct	identity_dictionary, dictionary
	ends
;[cf]
;[of]:initialize - release
;[of]:initialize
;[c]Initialize a dictionary
;[c]
;[c]ARGUMENTS
;[c]	eax	the dictionary
;[c]
identity_dictionary_initialize:

	mov	ebx, .identity_class
	jmp	dictionary_initialize
	
	align	4
.identity_class:	dd	.hash
	dd	.equals
	
.equals:	cmp	eax, ebx	; key are equals if and only if it is the same reference
.hash:	ret		; the hash is the value itself
;[cf]
;[of]:release
;[c]Release a dictionary
;[c]
;[c]ARGUMENTS
;[c]	eax	the dictionary
;[c]
identity_dictionary_release	=	dictionary_release
;[cf]
;[cf]
;[cf]
;[of]:string dictionary
;[of]:definitions
	struct	string_dictionary, dictionary
	ends
;[cf]
;[of]:initialize - release
;[of]:initialize
;[c]Initialize a dictionary
;[c]
;[c]ARGUMENTS
;[c]	eax	the dictionary
;[c]
string_dictionary_initialize:

	mov	ebx, .class
	jmp	dictionary_initialize
	
	align	4
.class:	dd	string_get_hash
	dd	string_compare
;[cf]
;[of]:release
;[c]Release a dictionary
;[c]
;[c]ARGUMENTS
;[c]	eax	the dictionary
;[c]
string_dictionary_release	=	dictionary_release
;[cf]
;[cf]
;[cf]
;[cf]
;[of]:file
;[of]:file
;[of]:definitions
	struct	file
	attr	handle,	dword
	attr	error,	dword
	ends

OK	=	0
ERR_FILE	=	$100
;[cf]
;[of]:initialize - release
;[of]:open read
;[c]Open a file in read mode
;[c]
;[c]ARGUMENTS
;[c]	eax	the file
;[c]	ebx	the filename
;[c]
;[c]RETURN VALUES
;[c]	eax	the error code
;[c]	zf	1 if success (error code = 0)
;[c]
file_open_read:

	push	esi
	mov	esi, eax

	sub	eax, eax
	push	eax	; hTemplateFile
	push	eax	; dwFlagsAndAttributes
	push	OPEN_EXISTING	; dwCreationDisposition
	push	eax	; lpSecurityAttributes
	push	FILE_SHARE_READ	; dwShareMode
	push	GENERIC_READ	; dwDesiredAccess
	push	ebx	; lpFileName
	call	[CreateFileA]
	
	or	eax, eax
	jz	.error
	
	mov	[esi + file.handle], eax
	sub	eax, eax
	
.done:	pop	esi
	ret

.error:	call	translate_error
	jmp	.done
;[cf]
;[of]:open write
;[c]Open a file in write mode
;[c]
;[c]ARGUMENTS
;[c]	eax	the file
;[c]	ebx	the filename
;[c]
;[c]RETURN VALUES
;[c]	eax	the error code
;[c]	zf	1 if success (error code = 0)
;[c]
file_open_write:

	push	esi
	mov	esi, eax

	sub	eax, eax
	push	eax	; hTemplateFile
	push	FILE_ATTRIBUTE_NORMAL	; dwFlagsAndAttributes
	push	CREATE_ALWAYS	; dwCreationDisposition
	push	eax	; lpSecurityAttributes
	push	eax 	; dwShareMode
	push	GENERIC_WRITE	; dwDesiredAccess
	push	ebx	; lpFileName
	call	[CreateFileA]
	
	or	eax, eax
	jz	.error
	
	mov	[esi + file.handle], eax
	sub	eax, eax
	
.done:	pop	esi
	ret

.error:	call	translate_error
	jmp	.done
;[cf]
;[of]:close
;[c]Close a file
;[c]
;[c]ARGUMENTS
;[c]	eax	the file
;[c]
;[c]RETURN VALUES
;[c]	eax	error code
;[c]	zf	1 if no error
;[c]
file_close:

	push	esi
	mov	esi, eax
	
	push	[eax + file.handle]
	call	[CloseHandle]
	or	eax, eax
	jz	.error
	
	sub	eax, eax
.done:	pop	esi
	ret

.error:	call	translate_error
	jmp	.done
;[cf]
;[cf]
;[of]:input - output
;[of]:read
;[c]Read data from a file
;[c]
;[c]ARGUMENTS
;[c]	eax	the file
;[c]	ebx	the buffer
;[c]	ecx	the number of bytes to read
;[c]
;[c]RETURN VALUES
;[c]	eax	the error code
;[c]	ebx	the number of bytes read (0 if error)
;[c]	zf	1 if no error
;[c]
file_read:

	push	esi
	push	0
	mov	esi, eax
	mov	eax, esp
	
	push	0
	push	eax
	push	ecx
	push	ebx
	push	[esi + file.handle]
	call	[ReadFile]
	
.common:	or	eax, eax
	jz	.error
	
	sub	eax, eax
	
.done:	pop	ebx	; the number of bytes read
	pop	esi
	ret
	
.error:	call	translate_error
	jmp	.done
;[cf]
;[of]:write
;[c]Write data to the file
;[c]
;[c]ARGUMENTS
;[c]	eax	the file
;[c]	ebx	the buffer
;[c]	ecx	the number of bytes to write
;[c]
;[c]RETURN VALUES
;[c]	eax	the error code
;[c]	ebx	the number of bytes written (0 if error)
;[c]	zf	1 if no error
;[c]
file_write:

	push	esi
	push	0
	mov	esi, eax
	mov	eax, esp
	
	push	0
	push	eax
	push	ecx
	push	ebx
	push	[esi + file.handle]
	call	[WriteFile]
	jmp	file_read.common
;[cf]
;[of]:read all
;[c]Read the content of the file to a memory buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the file
;[c]	ebx	the memory buffer
;[c]
;[c]RETURN VALUES
;[c]	eax	the error code
;[c]	zf	1 if no error
;[c]
file_read_all:

	push	esi
	push	edi
	mov	esi, eax
	mov	edi, ebx
	
	push	0
	push	[esi + file.handle]
	call	[GetFileSize]
	cmp	eax, INVALID_FILE_SIZE
	jz	.error
	
	push	eax
	mov	ecx, eax
	sub	ebx, ebx
	mov	eax, edi
	call	memory_buffer_make_room
	mov	ebx, eax
	pop	ecx
	mov	eax, esi
	call	file_read
	
.done:	pop	edi
	pop	esi
	ret

.error:	call	translate_error
	jmp	.done
;[cf]
;[cf]
;[of]:positioning
;[of]:seek
;[c]Change the current read position
;[c]
;[c]ARGUMENTS
;[c]	eax	the file
;[c]	ebx	the mode
;[c]	ecx	the position
;[c]
;[c]RETURN VALUES
;[c]	eax	the error code
;[c]	ebx	the absolute position
;[c]	zf	1 if no error
;[c]
file_seek:

	push	ebx	; mode
	push	0	; lpDistanceToMoveHigh
	push	ecx	; lDistanceToMove
	push	[eax + file.handle]	; hFile
	call	[SetFilePointer]
	cmp	eax, INVALID_SET_FILE_POINTER
	jz	.error
	mov	ebx, eax
	sub	eax, eax
.done:	ret

.error:	call	translate_error
	jmp	.done
;[cf]
;[cf]
;[of]:testing
;[of]:is file error
;[c]Returns true if the error code is a file error
;[c]
;[c]ARGUMENTS
;[c]	eax	the error code
;[c]
;[c]RETURN VALUES
;[c]	zf	1 if the error code is a file error
;[c]
;[c]REMARKS
;[c]	All registers except flags are saved.
;[c]
is_file_error:

	cmp	eax, ERR_FILE
	ret
;[cf]
;[cf]
;[of]:private
;[of]:translate error
;[c]Convert a windows error to an internal error code and save the error
;[c]
;[c]ARGUMENTS
;[c]	esi	the file
;[c]
;[c]RETURN VALUES
;[c]	eax	the error code
;[c]	zf	0
;[c]
private translate_error

	call	[GetLastError]
	mov	[esi + file.error], eax
	mov	eax, ERR_FILE
	or	eax, eax
	ret
;[cf]
;[cf]
;[cf]
;[of]:stdout
;[of]:puts
;[c]Writes text to stdout and append a new line
;[c]
;[c]ARGUMENTS
;[c]	eax	the string to write
;[c]
puts:
	call	print
	mov	eax, .newline
	jmp	print
	
.newline	db	13, 10, 0
;[cf]
;[of]:print
;[c]Writes text to stdout
;[c]
;[c]ARGUMENTS
;[c]	eax	the string to write
;[c]
print:	push	eax
	
	call	string_get_size
	push	eax
	
	push	STD_OUTPUT_HANDLE
	call	[GetStdHandle]
	pop	ecx
	pop	ebx
	
	push	0
	mov	edx, esp
	
	push	0
	push	edx
	push	ecx
	push	ebx
	push	eax
	call	[WriteFile]
	
	add	esp, 4
	ret
;[cf]
;[cf]
;[cf]

;[of]:fas2obj
;[of]:application
;[of]:main
main:	push	esi
	push	edi
	
;[of]:	Initialize
	sub	eax, eax
	mov	[fas_name], eax
	mov	[obj_name], eax
	mov	[dbg_name], eax
	mov	[opt_help], eax
	
	lea	eax, [fas]
	call	fas_initialize
	lea	eax, [obj]
	call	obj_initialize
	lea	eax, [cv8]
	lea	ebx, [fas]
	lea	ecx, [obj]
	call	cv8_initialize
;[cf]
;[of]:	Read command line

	lea	eax, [args]
	call	command_line_initialize
;[c]	
;[c]	Read arguments loop
;[c]	
.next_arg:	lea	eax, [args]
	call	command_line_get_next
	jz	.no_more_arg
	
	; Is it an option ?
	cmp	byte [eax], "/"
	jz	.option
	cmp	byte [eax], "-"
	jz	.option
	
	; fas name
	cmp	[fas_name], 0
	jnz	.has_fas
	call	string_copy
	mov	[fas_name], eax
	jmp	.next_arg
.has_fas:	
	; obj name
	cmp	[obj_name], 0
	jnz	.has_obj
	call	string_copy
	mov	[obj_name], eax
	jmp	.next_arg
.has_obj:	
	; obj debug name
	cmp	[dbg_name], 0
	jnz	.has_dbg
	call	string_copy
	mov	[dbg_name], eax
	jmp	.next_arg
.has_dbg:	
	jmp	error_too_many_arguments
;[c]	
;[c]	Read options
;[c]	
.option:	inc	eax
	cmp	byte [eax], "?"
	jz	.help
	cmp	byte [eax], "h"
	jz	.help
	
	jmp	error_invalid_option
	
.help:	; Set help flag
	inc	[opt_help]
	
	; Ignore all other options: don't jump back to next_arg
;[c]	
;[c]	All done, check arguments
;[c]	
.no_more_arg:	lea	eax, [args]
	call	command_line_release
;[cf]
;[of]:	Check command line
	; help ?
	cmp	[opt_help], 0
	jz	.no_help
	call	print_usage
	jmp	.ok
.no_help:

	; fas name provided ?
	cmp	[fas_name], 0
	jz	error_no_fas
	
	; obj name provided ?
	cmp	[obj_name], 0
	jz	error_no_obj
	
	; dbg name provided ?
	cmp	[dbg_name], 0
	jnz	.1
	mov	eax, [obj_name]
	call	string_copy
	mov	[dbg_name], eax
.1:	
;[cf]
	
;[of]:	Load fas

	lea	eax, [fas]
	mov	ebx, [fas_name]
	call	fas_load
;[cf]
;[of]:	Load obj

	lea	eax, [obj]
	mov	ebx, [obj_name]
	call	obj_load
;[cf]
;[of]:	Already a debug section ?

	lea	eax, [obj]
	mov	ebx, s_debug_s
	call	obj_find_section
	jz	error_debug_found
	
	lea	eax, [obj]
	mov	ebx, s_debug_t
	call	obj_find_section
	jz	error_debug_found
;[cf]
;[of]:	Create the .debug$T section

	; Add a .debug$T section
	lea	eax, [obj]
	mov	ebx, s_debug_t
	mov	ecx, DEBUG_SECTION_FLAGS
	call	obj_add_section

	; Assign it the data
	mov	ebx, eax
	mov	ecx, default_debug_t
	mov	edx, sizeof_default_debug_t
	lea	eax, [obj]
	call	obj_section_add_raw_data
;[cf]
;[of]:	Create the .debug$S section

	lea	eax, [cv8]
	call	cv8_write_debug_s

	lea	eax, [obj]
	mov	ebx, s_debug_s
	mov	ecx, DEBUG_SECTION_FLAGS
	call	obj_add_section
	mov	[debug_s], eax
	
	mov	ebx, [debug_s]
	mov	ecx, [cv8 + cv8.raw + memory_buffer.p]
	mov	edx, [cv8 + cv8.raw + memory_buffer.used]
	lea	eax, [obj]
	call	obj_section_add_raw_data
	
	mov	ebx, [debug_s]
	mov	ecx, [cv8 + cv8.relocations + memory_buffer.p]
	mov	edx, [cv8 + cv8.num_of_relocations]
	lea	eax, [obj]
	call	obj_section_add_relocations
;[cf]
;[of]:	Save the debug obj file

	lea	eax, [obj]
	mov	ebx, [dbg_name]
	call	obj_save
;[cf]
	
.ok:	sub	eax, eax
	
;[of]:	Release
.release:

	push	eax
	
	lea	eax, [cv8]
	call	cv8_release
	
	lea	eax, [obj]
	call	obj_release
	
	lea	eax, [fas]
	call	fas_release
	
	mov	eax, [fas_name]
	call	string_delete
	
	mov	eax, [obj_name]
	call	string_delete
	
	mov	eax, [dbg_name]
	call	string_delete
	
	pop	eax
;[cf]
	
	pop	edi
	pop	esi
	ret
	
;[of]:	error debug found
error_debug_found:

	mov	eax, s_error_debug_found
	jmp	fatal_error
;[cf]
;[of]:	invalid option
error_invalid_option:

	call	print_usage
	
	mov	eax, s_error_invalid_option
	jmp	fatal_error
;[cf]
;[of]:	too many arguments
error_too_many_arguments:

	call	print_usage
	
	mov	eax, s_error_too_many_arguments
	jmp	fatal_error
;[cf]
;[of]:	no fas
error_no_fas:

	call	print_usage
	
	mov	eax, s_error_no_fas
	jmp	fatal_error
;[cf]
;[of]:	no obj
error_no_obj:

	call	print_usage
	
	mov	eax, s_error_no_obj
	jmp	fatal_error
;[cf]

;[cf]
;[of]:print usage
print_usage:

	mov	eax, s_usage
	jmp	puts
;[cf]
;[cf]
;[of]:fas
;[of]:definitions
;[of]:fas
	struct	fas
	attr	buffer,	memory_buffer	; the raw content of the file
	attr	string_table,	dword	; the table of strings
	attr	preprocessed_source,	dword	; preprocessed source
	attr	input_filename,	dword	; the input filename
	attr	output_filename,	dword	; the output filename
	attr	symbols,	dword	; the table of symbols
	attr	rows,	dword	; the table of rows
	attr	num_of_symbols,	dword	; number of symbols
	attr	num_of_rows,	dword	; number of rows
	attr	files,	identity_dictionary	; the list of used files
	ends			
				
	struct	fas_symbol		
	attr	name,	dword	
	attr	section,	dword	
	attr	usable,	dword	
	attr	value,	qword	
	attr	size_data,	dword	
	ends			
				
	struct	fas_row_dump		
	attr	section,	dword	
	attr	offset,	dword	
	attr	filename,	dword	
	attr	line_number,	dword	
	attr	offset_output,	dword	
	ends			
;[cf]
;[of]:raw fas
	struct	header
	attr	signature,	dword
	attr	major,	byte
	attr	minor,	byte
	attr	length_header,	word
	attr	offset_input_file_name,	dword
	attr	offset_output_file_name,	dword
	attr	offset_string_table,	dword
	attr	length_string_table,	dword
	attr	offset_symbol_table,	dword
	attr	length_symbol_table,	dword
	attr	offset_preprocessed_source,	dword
	attr	length_preprocessed_source,	dword
	attr	offset_assembly_dump,	dword
	attr	length_assembly_dump,	dword
	attr	offset_section_table,	dword
	attr	length_section_table,	dword
	ends		
			
	struct	symbol	
	attr	value,	qword
	attr	flags,	word
	attr	size_data,	byte
	attr	type,	byte
	attr	ext_sib,	dword
	attr	pass_num_def,	word
	attr	pass_num_use,	word
	attr	section,	dword
	attr	name,	dword
	attr	line_prep_offset,	dword
	ends		
			
	struct	row_dump	
	attr	offset_output,	dword
	attr	offset_prep_line,	dword
	attr	address,	qword
	attr	ext_sib,	dword
	attr	section,	dword
	attr	type_address,	byte
	attr	type_code,	byte
	attr	virtual_bits,	byte
	attr	reserved,	byte
	ends		
			
	struct	preprocessed_line	
	attr	offset_file_name,	dword
	attr	line_number,	dword
	attr	position,	dword
	attr	offset_prep_line_in_macro_def,	dword
	ends		
;[cf]
;[cf]
;[of]:initialize - release
;[of]:initialize
;[c]Initialize the fas file
;[c]
;[c]ARGUMENTS
;[c]	eax	the fas object
;[c]
fas_initialize:

	push	esi
	mov	esi, eax
	
	mov	[esi + fas.symbols], 0
	mov	[esi + fas.num_of_rows], 0
	
	lea	eax, [esi + fas.buffer]
	call	memory_buffer_initialize
	
	lea	eax, [esi + fas.files]
	call	identity_dictionary_initialize
	
	pop	esi
	ret
;[cf]
;[of]:release
;[c]Releases the fas object
;[c]
;[c]ARGUMENTS
;[c]	eax	the fas object
;[c]
fas_release:

	push	esi
	mov	esi, eax
	
	lea	eax, [esi + fas.files]
	call	identity_dictionary_release
	
	mov	eax, [esi + fas.symbols]
	call	free_memory_with_size
	
	lea	eax, [esi + fas.buffer]
	call	memory_buffer_release
	
	pop	esi
	ret
;[cf]
;[cf]
;[of]:loading
;[of]:load
;[c]Load a fas file
;[c]
;[c]ARGUMENTS
;[c]	eax	the fas object
;[c]	ebx	the filename
;[c]
fas_load:

	pushad
	mov	esi, eax

	mov	eax, ebx
	call	fas_load_file
	call	fas_load_header
	call	fas_load_symbols
	call	fas_load_rows

	popad
	ret

;[of]:load file
;[c]Load the fas file
;[c]
;[c]ARGUMENTS
;[c]	esi	the fas object
;[c]	eax	the filename
;[c]	
fas_load_file:

	push	ebp
	mov	ebx, eax
	
	locals
	local	.file,	file
	endl

	lea	eax, [.file]
	call	file_open_read
	jnz	error_cant_open_fas
	lea	eax, [.file]
	lea	ebx, [esi + fas.buffer]
	call	file_read_all
	jnz	error_cant_read_fas
	lea	eax, [.file]
	call	file_close

	mov	esp, ebp
	pop	ebp
	ret
;[cf]
;[of]:load header
;[c]Load header
;[c]
fas_load_header:

	mov	edi, [esi + fas.buffer + memory_buffer.p]
	
	; Check signature
	mov	eax, [edi + header.signature]
	and	eax, not 1
	cmp	eax, $1A736166
	jnz	error_invalid_fas
	
	; Get address of string table
	mov	eax, [edi + header.offset_string_table]
	add	eax, edi
	mov	[esi + fas.string_table], eax
	
	; Get address of string table
	mov	eax, [edi + header.offset_preprocessed_source]
	add	eax, edi
	mov	[esi + fas.preprocessed_source], eax
	
	; Get the input file name
	mov	eax, [edi + header.offset_input_file_name]
	add	eax, [esi + fas.string_table]
	mov	[esi + fas.input_filename], eax
	call	register_file
	
	; Get the output file name
	mov	eax, [edi + header.offset_output_file_name]
	add	eax, [esi + fas.string_table]
	mov	[esi + fas.output_filename], eax

	ret
;[cf]
;[of]:load symbols
;[c]Load symbols
;[c]
fas_load_symbols:

	mov	edi, [esi + fas.buffer + memory_buffer.p]
	
	; Get number of symbols
	mov	eax, [edi + header.length_symbol_table]
	sub	edx, edx
	mov	ebx, sizeof.symbol
	div	ebx
	mov	[esi + fas.num_of_symbols], eax
	
	; Allocate array
	mov	ebx, sizeof.fas_symbol
	mul	ebx
	call	get_memory_with_size
	mov	[esi + fas.symbols], eax
	
	add	edi, [edi + header.offset_symbol_table]
	mov	ecx, [esi + fas.num_of_symbols]
	mov	edx, eax
	jecxz	.2
.loop:	push	ecx

	; Get name
	mov	eax, [edi + symbol.name]
	push	edx
	call	.get_string
	pop	edx
	mov	[edx + fas_symbol.name], eax
	
	; Value
	mov	eax, dword [edi + symbol.value]
	mov	ebx, dword [edi + symbol.value + 4]
	mov	dword [edx + fas_symbol.value], eax
	mov	dword [edx + fas_symbol.value + 4], ebx
	
	; Section
	mov	eax, [edi + symbol.section]
	and	eax, not $80000000
	mov	[edx + fas_symbol.section], eax
	
	; Size
	movzx	eax, [edi + symbol.size_data]
	mov	[edx + fas_symbol.size_data], eax
	
	; usable
	sub	eax, eax
	cmp	[edi + symbol.type], 2
	jnz	.n
	test	[edi + symbol.section], $80000000
	jnz	.n
	inc	eax
.n:	mov	[edx + fas_symbol.usable], eax
	
	pop	ecx
	add	edx, sizeof.fas_symbol
	add	edi, sizeof.symbol
	loop	.loop
.2:	
	ret

.get_string:

	test	eax, $80000000
	jz	.pascal
	and	eax, not $80000000
	add	eax, [esi + fas.string_table]
	jmp	string_copy

.pascal:

	add	eax, [esi + fas.preprocessed_source]
	movzx	ebx, byte [eax]
	inc	eax
	jmp	string_copy_range
;[cf]
;[of]:load rows
;[c]Load rows
;[c]
;[c]ARGUMENTS
;[c]	esi	the fas object
;[c]
fas_load_rows:

	mov	edi, [esi + fas.buffer + memory_buffer.p]
	
	; Get number of rows
	mov	eax, [edi + header.length_assembly_dump]
	sub	edx, edx
	mov	ebx, sizeof.row_dump
	div	ebx
	push	eax
	
	; Allocate array
	; This array is bigger than what we really need because we remove
	; duplicates but it is the easiest way to avoid multiples allocations
	mov	ebx, sizeof.fas_row_dump
	mul	ebx
	call	get_memory_with_size
	mov	[esi + fas.rows], eax
	
	add	edi, [edi + header.offset_assembly_dump]
	pop	ecx
	mov	edx, eax
	jecxz	.2
.loop:	push	ecx

	; Skip unusable rows
	cmp	[edi + row_dump.virtual_bits], 0
	jnz	.skip
	cmp	[edi + row_dump.type_address], 2
	jnz	.skip
	test	[edi + row_dump.section], $80000000
	jnz	.skip
	cmp	[edi + row_dump.section], 0
	jz	.skip
	
	; Get the right slot
	cmp	edx, [esi + fas.rows]	; first row ?
	jz	.1	; use the first slot
	sub	edx, sizeof.fas_row_dump	; go to previous slot
	mov	eax, [edx + fas_row_dump.offset_output]	; same output ?
	cmp	eax, [edi + row_dump.offset_output]	; override previous row
	jz	.1	;
	add	edx, sizeof.fas_row_dump	; advance to next slot
.1:	
	; Section
	mov	eax, [edi + row_dump.section]
	and	eax, not $80000000
	mov	[edx + fas_row_dump.section], eax
	
	; Offset output
	mov	eax, [edi + row_dump.offset_output]
	mov	[edx + fas_row_dump.offset_output], eax
	
	; Source line
	mov	eax, [edi + row_dump.offset_prep_line]
	jmp	.first
.follow:	mov	eax, [eax + preprocessed_line.position]
.first:	add	eax, [esi + fas.preprocessed_source]
	test	[eax + preprocessed_line.line_number], $80000000
	jnz	.follow
	
	mov	[edx + fas_row_dump.offset], eax
	mov	ebx, [eax + preprocessed_line.line_number]
	mov	[edx + fas_row_dump.line_number], ebx
	mov	eax, [eax + preprocessed_line.offset_file_name]
	or	eax, eax
	jnz	.ext
	mov	eax, [esi + fas.input_filename]
	jmp	.set
.ext:	add	eax, [esi + fas.preprocessed_source]
	push	eax
	push	edx
	call	register_file
	pop	edx
	pop	eax
.set:	mov	[edx + fas_row_dump.filename], eax
	
	; Advance to next slot
	add	edx, sizeof.fas_row_dump
	
.skip:	pop	ecx
	add	edi, sizeof.row_dump
	loop	.loop
.2:
;[c]
;[c]	Compute number of inserted rows
;[c]
	sub	edx, [esi + fas.rows]
	mov	eax, edx
	sub	edx, edx
	mov	ebx, sizeof.fas_row_dump
	div	ebx
	mov	[esi + fas.num_of_rows], eax
	
	ret
;[cf]

;[of]:error cant open fas
error_cant_open_fas:

	mov	eax, s_error_open_input_file
	jmp	fatal_error
;[cf]
;[of]:error cant read fas
error_cant_read_fas:

	mov	eax, s_error_read_input_file
	jmp	fatal_error
;[cf]
;[of]:error invalid fas
error_invalid_fas:

	mov	eax, s_error_invalid_fas
	jmp	fatal_error
;[cf]
;[cf]
;[cf]
;[of]:enumerating
;[of]:each symbol
;[c]Enumerates all symbols
;[c]
;[c]ARGUMENTS
;[c]	eax	the fas objct
;[c]	ebx	user argument
;[c]	ecx	the function to call for each element
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]RETURN VALUES
;[c]	esi	user argument
;[c]	edi	user argument
;[c]	ebp	user argument
;[c]
;[c]REMARKS
;[c]	User arguments are passed to the function and can be modified.
;[c]
fas_each_symbol:

	mov	edx, ecx

	mov	ecx, [eax + fas.num_of_symbols]
	mov	eax, [eax + fas.symbols]
	jecxz	.done
	
.loop:	push	eax
	push	ebx
	push	ecx
	push	edx
	call	edx
	pop	edx
	pop	ecx
	pop	ebx
	pop	eax
	add	eax, sizeof.fas_symbol
	loop	.loop

.done:	ret
;[cf]
;[cf]
;[of]:private
;[of]:register file
;[c]Register a new filename
;[c]
;[c]ARGUMENTS
;[c]	esi	the fas object
;[c]	eax	the filename
;[c]
register_file:

	mov	ecx, eax	; no values, we're just interested with keys
	mov	ebx, eax	
	lea	eax, [esi + fas.files]	
	jmp	dictionary_add
;[cf]
;[cf]
;[cf]
;[of]:obj
;[of]:definitions
;[of]:obj
	struct	obj
	attr	machine,	word	; machine type
	attr	time_date_stamp,	dword	
	attr	num_of_sections,	dword	; number of sections
	attr	num_of_symbols,	dword	; number of symbols
	attr	characteristics,	word	;
				
	attr	optional_header,	memory_buffer	; the optional header
	attr	section_table,	memory_buffer	; the section table
	attr	raw_data,	memory_buffer	; the raw data
	attr	symbol_table,	memory_buffer	; the symbol table
	attr	symbol_list,	string_dictionary	; hash table to symbols
				
	ends			
;[c]				
	struct	obj_section		
	attr	name,	dword	
	attr	virtual_size,	dword	
	attr	virtual_address,	dword	
	attr	size_of_raw_data,	dword	
	attr	pointer_to_raw_data,	dword	
	attr	pointer_to_relocations,	dword	
	attr	pointer_to_line_numbers,	dword	
	attr	number_of_relocations,	word	
	attr	number_of_line_numbers,	word	
	attr	characteristics,	dword	
	attr	output_offset,	dword	
	attr	symbol_index,	dword	; index of section name in symbol table
	ends			
;[c]				
	struct	obj_symbol		
	attr	name,	dword	
	attr	value,	dword	
	attr	section_number,	word	
	attr	type,	word	
	attr	storage_class,	byte	
	attr	number_of_aux_symbols,	byte	
	ends
;[cf]
;[of]:coff structures
	struct	obj_header
	attr	Machine,	word
	attr	NumberOfSections,	word
	attr	TimeDateStamp,	dword
	attr	PointerToSymbolTable,	dword
	attr	NumberOfSymbols,	dword
	attr	SizeOfOptionalHeader,	word
	attr	Characteristics,	word
	ends		
			
	struct	section_header	
	attr	Name,	byte, 8
	attr	VirtualSize,	dword
	attr	VirtualAddress,	dword
	attr	SizeOfRawData,	dword
	attr	PointerToRawData,	dword
	attr	PointerToRelocations,	dword
	attr	PointerToLinenumbers,	dword
	attr	NumberOfRelocations,	word
	attr	NumberOfLinenumbers,	word
	attr	Characteristics,	dword
	ends		
			
	struct	relocation_item	
	attr	VirtualAddress,	dword
	attr	SymbolTableIndex,	dword
	attr	Type,	word
	ends		
			
	struct	symbol_table_item	
	attr	Name,	byte, 8	; 8 chars
	attr	Value,	dword	
	attr	SectionNumber,	word	
	attr	Type,	word	
	attr	StorageClass,	byte	
	attr	NumberOfAuxSymbols,	byte	
	ends			
;[c]	
;[c]	CodeView
;[c]	
	struct	compile_sym		
	attr	reclen,	word	
	attr	rectyp,	word	
	attr	flags,	dword	
	attr	machine,	word	
	attr	verFEMajor,	word	
	attr	verFEMinor,	word	
	attr	verFEBuild,	word	
	attr	verMajor,	word	
	attr	verMinor,	word	
	attr	verBuild,	word	
	ends			
				
	struct	label_sym_32		
	attr	reclen,	word	
	attr	rectyp,	word	
	attr	off,	dword	
	attr	seg,	word	
	attr	flags,	byte	; (CV_PROCFLAGS) flags
	attr	name,	byte, 0	;
	ends			
				
	struct	datas_sym_32		
	attr	reclen,	word	; Record length [SYMTYPE]
	attr	rectyp,	word	; S_LDATA32, S_GDATA32 or S_PUB32
	attr	typind,	dword	; type index
	attr	off,	dword	;
	attr	seg,	word	;
	attr	name,	byte, 0	;
	ends
;[cf]
;[of]:coff constants
supported_machine_id	=	$14c
	
; Section flags	
ALIGN_1BYTES	=	$00100000
ALIGN_2BYTES	=	$00200000
ALIGN_4BYTES	=	$00300000
ALIGN_8BYTES	=	$00400000
ALIGN_16BYTES	=	$00500000
		
CNT_CODE	=	$00000020
CNT_INITIALIZED_DATA	=	$00000040
CNT_UNINITIALIZED_DATA	=	$00000080
MEM_DISCARDABLE	=	$02000000
MEM_EXECUTE	=	$20000000
MEM_READ	=	$40000000
MEM_WRITE	=	$80000000
DEBUG_SECTION_FLAGS	=	ALIGN_1BYTES or CNT_INITIALIZED_DATA or MEM_DISCARDABLE or MEM_READ

; Relocation types
I386_DIR32	=	$0006
I386_SECTION	=	$000A
I386_SECREL	=	$000B

; rec type
S_OBJNAME	=	$0001101
S_THUNK32	=	$0001102
S_BLOCK32	=	$0001103
S_WITH32	=	$0001104
S_LABEL32	=	$0001105
S_REGISTER	=	$0001106
S_CONSTANT	=	$0001107
S_UDT	=	$0001108
S_MANYREG	=	$000110A
S_BPREL32	=	$000110B
S_LDATA32	=	$000110C
S_GDATA32	=	$000110D
S_PUB32	=	$000110E
S_LPROC32	=	$000110F
S_GPROC32	=	$0001110
S_REGREL32	=	$0001111
S_LTHREAD32	=	$0001112
S_GTHREAD32	=	$0001113
S_LPROCMIPS	=	$0001114
S_GPROCMIPS	=	$0001115
S_COMPILE2	=	$0001116
S_MANYREG2	=	$0001117
S_LPROCIA64	=	$0001118
S_GPROCIA64	=	$0001119

; .debug$S subsection types
DEBUG_S_SYMBOLS	=	$F1
DEBUG_S_LINES	=	$F2
DEBUG_S_STRINGTABLE	=	$F3
DEBUG_S_FILECHKSMS	=	$F4
DEBUG_S_FRAMEDATA	=	$F5

; CV_PROCFLAGS
CV_PFLAG_NOFPO	=	$01	; frame pointer present
CV_PFLAG_INT	=	$02	; interrupt return
CV_PFLAG_FAR	=	$04	; far return
CV_PFLAG_NEVER	=	$08	; function does not return
CV_PFLAG_NOTREACHED	=	$10	; label isn't fallen into
CV_PFLAG_CUST_CALL	=	$20	; custom calling convention
CV_PFLAG_NOINLINE	=	$40	; function marked as noinline
CV_PFLAG_OPTDBGINFO	=	$80	; function has debug information for optimized code

; TYPES
T_PVOID	=	$0103	; near pointer to void
T_PFVOID	=	$0203	; far pointer to void
T_PHVOID	=	$0303	; huge pointer to void
T_32PVOID	=	$0403	; 32 bit pointer to void
T_64PVOID	=	$0603	; 64 bit pointer to void
T_PASCHAR	=	$0061	; Pascal CHAR
T_CHAR	=	$0010	; 8 bit signed
T_UCHAR	=	$0020	; 8 bit unsigned
T_RCHAR	=	$0070	; really a char
T_WCHAR	=	$0071	; wide char
T_INT1	=	$0068	; 8 bit signed int
T_UINT1	=	$0069	; 8 bit unsigned int
T_SHORT	=	$0011	; 16 bit signed
T_USHORT	=	$0021	; 16 bit unsigned
T_INT2	=	$0072	; 16 bit signed int
T_UINT2	=	$0073	; 16 bit unsigned int
T_LONG	=	$0012	; 32 bit signed
T_ULONG	=	$0022	; 32 bit unsigned
T_INT4	=	$0074	; 32 bit signed int
T_UINT4	=	$0075	; 32 bit unsigned int
T_QUAD	=	$0013	; 64 bit signed
T_UQUAD	=	$0023	; 64 bit unsigned
T_INT8	=	$0076	; 64 bit signed int
T_UINT8	=	$0077	; 64 bit unsigned int
T_OCT	=	$0014	; 128 bit signed
T_UOCT	=	$0024	; 128 bit unsigned
;[cf]
;[cf]
;[of]:initialize - release
;[of]:initialize
;[c]Initialize the obj file
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj object
;[c]
obj_initialize:

	push	esi
	mov	esi, eax
	
	lea	eax, [esi + obj.optional_header]
	call	memory_buffer_initialize
	lea	eax, [esi + obj.section_table]
	call	memory_buffer_initialize
	lea	eax, [esi + obj.raw_data]
	call	memory_buffer_initialize
	lea	eax, [esi + obj.symbol_table]
	call	memory_buffer_initialize
	
	lea	eax, [esi + obj.symbol_list]
	call	string_dictionary_initialize

	pop	esi
	ret
;[cf]
;[of]:release
;[c]Releases the obj object
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj object
;[c]
obj_release:

	push	esi
	mov	esi, eax
	
	lea	eax, [esi + obj.symbol_list]
	call	string_dictionary_release

	lea	eax, [esi + obj.optional_header]
	call	memory_buffer_release
	lea	eax, [esi + obj.section_table]
	call	memory_buffer_release
	lea	eax, [esi + obj.raw_data]
	call	memory_buffer_release
	lea	eax, [esi + obj.symbol_table]
	call	memory_buffer_release
	
	pop	esi
	ret
;[cf]
;[cf]
;[of]:loading - saving
;[of]:load
;[c]Load a obj file
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj object
;[c]	ebx	the filename
;[c]
obj_load:

	pushad
	mov	esi, eax
	
	locals
	local	.obj,	dword	; save self
	local	.filename,	dword	; the name of the obj file to load
	local	.file,	file	; input file
	local	.buffer,	memory_buffer	; store content
	local	.sections,	dword	; section table
	local	.symbols,	dword	; symbol table
	local	.strings,	dword	; string table
	local	.raw_data,	dword	; offset of raw data
				
	local	.index,	dword	; the current index in the symbol table
	local	.offset,	dword	; current offset of raw data
	endl

	call	obj_load_initialize
	call	obj_load_file
	call	obj_load_header
	call	load_obj_sections
	call	load_obj_symbols
	call	obj_load_release
	
	mov	esp, ebp
	popad
	ret

;[of]:load initialize
obj_load_initialize:

	mov	[obj_load.obj], esi
	mov	[obj_load.filename], ebx
	
	lea	eax, [obj_load.buffer]
	jmp	memory_buffer_initialize

;[cf]
;[of]:load release
obj_load_release:

	lea	eax, [obj_load.buffer]
	jmp	memory_buffer_release
;[cf]
;[of]:load file
;[c]Load the obj file
;[c]
;[c]ARGUMENTS
;[c]	esi	the obj object
;[c]
obj_load_file:

	; Open
	lea	eax, [obj_load.file]
	mov	ebx, [obj_load.filename]
	call	file_open_read
	jnz	error_cant_open_obj

	; Read
	lea	eax, [obj_load.file]
	lea	ebx, [obj_load.buffer]
	call	file_read_all
	jnz	error_cant_read_obj
	
	; Close
	lea	eax, [obj_load.file]
	jmp	file_close
;[cf]
;[of]:load obj header
;[c]Load header
;[c]
;[c]ARGUMENTS
;[c]	esi	the obj
;[c]
obj_load_header:

	mov	edi, [obj_load.buffer + memory_buffer.p]
	
	; Cet machine type
	mov	ax, [edi + obj_header.Machine]
	cmp	ax, supported_machine_id
	jnz	error_invalid_machine
	mov	[esi + obj.machine], ax
	
	; Get number of sections
	movzx	eax, word [edi + obj_header.NumberOfSections]
	mov	[esi + obj.num_of_sections], eax
	
	; Get number of symbols
	mov	eax, [edi + obj_header.NumberOfSymbols]
	mov	[esi + obj.num_of_symbols], eax
	
	; Other attributes ...
	mov	eax, [edi + obj_header.TimeDateStamp]
	mov	cx, [edi + obj_header.Characteristics]
	mov	[esi + obj.time_date_stamp], eax
	mov	[esi + obj.characteristics], cx
	
	; Save the base address of symbols
	mov	ebx, [edi + obj_header.PointerToSymbolTable]
	add	ebx, edi
	mov	[obj_load.symbols], ebx
	
	; Save the base address of strings
	mov	eax, sizeof.symbol_table_item
	mul	[esi + obj.num_of_symbols]
	add	ebx, eax
	mov	[obj_load.strings], ebx
	
	; Load the optional header
	lea	ebx, [edi + sizeof.obj_header]
	movzx	ecx, [edi + obj_header.SizeOfOptionalHeader]
	lea	edi, [ebx + ecx]	; the address of sections
	lea	eax, [esi + obj.optional_header]
	call	memory_buffer_add_bytes

	; Save the base address of sections
	mov	[obj_load.sections], edi
	
	; Save the raw data
	;  start: after section
	;  end: to symbols
	mov	eax, sizeof.section_header
	mul	[esi + obj.num_of_sections]
	lea	ebx, [eax + edi]
	mov	ecx, [obj_load.symbols]
	sub	ecx, ebx
	lea	eax, [esi + obj.raw_data]
	push	ebx
	call	memory_buffer_add_bytes
	pop	ebx
	
	; Save offset to raw_data (to correct pointers in section table)
	sub	ebx, [obj_load.buffer + memory_buffer.p]
	mov	[obj_load.raw_data], ebx

	ret
;[cf]
;[of]:load obj sections
;[c]Load sections
;[c]
;[c]ARGUMENTS
;[c]	esi	the obj
;[c]
load_obj_sections:

;[c]	
;[c]	Reserve space for obj_sections in the section table buffer
;[c]	
	mov	eax, [esi + obj.num_of_sections]
	mov	ebx, sizeof.obj_section
	mul	ebx
	mov	ecx, eax
	sub	ebx, ebx
	lea	eax, [esi + obj.section_table]
	call	memory_buffer_make_room
;[c]	
;[c]	Initialize all obj_section
;[c]	
	push	esi
	mov	ecx, [esi + obj.num_of_sections]
	mov	edi, [obj_load.sections]
	mov	esi, eax
	jecxz	.done
.loop:	push	ecx
	call	load_section
	pop	ecx
	add	edi, sizeof.section_header
	add	esi, sizeof.obj_section
	loop	.loop
.done:	mov	eax, esi
	pop	esi

	ret

;[of]:load section
load_section:

	mov	eax, [edi + section_header.VirtualSize]
	mov	ebx, [edi + section_header.VirtualAddress]
	mov	ecx, [edi + section_header.SizeOfRawData]
	mov	[esi + obj_section.virtual_size], eax
	mov	[esi + obj_section.virtual_address], ebx
	mov	[esi + obj_section.size_of_raw_data], ecx
	
	
	mov	eax, [edi + section_header.PointerToRawData]
	call	fixup_pointer
	mov	[esi + obj_section.pointer_to_raw_data], eax
	
	mov	eax, [edi + section_header.PointerToRelocations]
	call	fixup_pointer
	mov	[esi + obj_section.pointer_to_relocations], eax
	
	mov	eax, [edi + section_header.PointerToLinenumbers]
	call	fixup_pointer
	mov	[esi + obj_section.pointer_to_line_numbers], eax
	
	mov	ax, [edi + section_header.NumberOfRelocations]
	mov	bx, [edi + section_header.NumberOfLinenumbers]
	mov	ecx, [edi + section_header.Characteristics]
	mov	[esi + obj_section.number_of_relocations], ax
	mov	[esi + obj_section.number_of_line_numbers], bx
	mov	[esi + obj_section.characteristics], ecx
	
	mov	eax, [edi + section_header.PointerToRawData]
	mov	[esi + obj_section.output_offset], eax
	
	; Read name
	cmp	byte [edi + section_header.Name], "/"
	jnz	.1
	int	3	; // unhandled yet
	jmp	.2
.1:	lea	eax, [edi + section_header.Name]
	call	read_obj_symbol_name
	mov	[esi + obj_section.name], eax
.2:
	ret


;[of]:fixup pointer
;[c]Converts a pointer to an offset relative to the beginning of the raw_data buffer
;[c]
;[c]ARGUMENTS
;[c]	eax	the pointer
;[c]	ebp	the locals
;[c]
;[c]RETURN VALUES
;[c]	eax	the offset or -1, if pointer not set
;[c]
fixup_pointer:

	or	eax, eax
	jz	.no_value
	sub	eax, [obj_load.raw_data]
	ret

.no_value:

	dec	eax
	ret
;[cf]
;[cf]
;[cf]
;[of]:load obj symbols
;[c]Load symbols
;[c]
;[c]ARGUMENTS
;[c]	esi	the obj
;[c]
load_obj_symbols:

;[c]	
;[c]	Reserve space for symbols in the section table buffer
;[c]	
	mov	eax, [esi + obj.num_of_symbols]
	mov	ebx, sizeof.obj_symbol
	mul	ebx
	mov	ecx, eax
	sub	ebx, ebx
	lea	eax, [esi + obj.symbol_table]
	call	memory_buffer_make_room
;[c]	
;[c]	Load all symbols
;[c]	
	push	esi
	mov	[obj_load.index], 0
	mov	[obj_load.offset], 0
	mov	edi, [obj_load.symbols]
	mov	ecx, [esi + obj.num_of_symbols]
	mov	esi, eax
	jecxz	.done
.loop:	push	ecx
	call	load_obj_symbol
	call	setup_symbol_index
	pop	ecx
	add	edi, sizeof.symbol_table_item
	add	esi, sizeof.obj_symbol
	inc	[obj_load.index]
	loop	.loop
.done:	pop	esi
;[c]	
;[c]	All done.
;[c]	
	ret

;[of]:load obj symbol
;[c]Load a symbol
;[c]
;[c]ARGUMENTS
;[c]	esi	the obj_symbol
;[c]	edi	the symbol_table_item
;[c]
load_obj_symbol:

	; Copy attributes
	mov	eax, [edi + symbol_table_item.Value]
	mov	bx, [edi + symbol_table_item.SectionNumber]
	mov	cx, [edi + symbol_table_item.Type]
	mov	dl, [edi + symbol_table_item.StorageClass]
	mov	dh, [edi + symbol_table_item.NumberOfAuxSymbols]
	mov	[esi + obj_symbol.value], eax
	mov	[esi + obj_symbol.section_number], bx
	mov	[esi + obj_symbol.type], cx
	mov	[esi + obj_symbol.storage_class], dl
	mov	[esi + obj_symbol.number_of_aux_symbols], dh
	
;[of]:	Read name
	cmp	dword [edi + symbol_table_item.Name], 0
	jz	.indir
	
	lea	eax, [edi + symbol_table_item.Name]
	call	read_obj_symbol_name
	jmp	.set

.indir:	mov	eax, dword [edi + symbol_table_item.Name + 4]
	add	eax, [obj_load.strings]
	call	string_copy
	
.set:	mov	[esi + obj_symbol.name], eax
;[cf]
	
	; Add the symbol to the hash table
	mov	ebx, [esi + obj_symbol.name]
	mov	ecx, esi
	mov	eax, [obj_load.obj]
	sub	ecx, [eax + obj.symbol_table + memory_buffer.p]	; store offset, not pointer
	lea	eax, [eax + obj.symbol_list]
	jmp	dictionary_add
;[cf]
;[of]:setup symbol index
;[c]Setup the symbol index of the associated section
;[c]
;[c]ARGUMENTS
;[c]	esi	the obj_symbol
;[c]	ebp	the locals
;[c]
setup_symbol_index:

	push	edi
	
	movsx	ebx, [esi + obj_symbol.section_number]	
	or	ebx, ebx	; ignore 0, -1, -2
	jle	.ignore	; 
	
	mov	eax, [obj_load.obj]
	call	obj_get_section_at_index
	mov	edi, eax
	
	mov	eax, [edi + obj_section.name]
	mov	ebx, [esi + obj_symbol.name]
	call	string_compare
	jnz	.ignore
	
	mov	eax, [obj_load.index]
	mov	[edi + obj_section.symbol_index], eax
	
.ignore:	pop	edi
	ret

;[cf]
;[cf]

;[of]:error cant open obj
error_cant_open_obj:

	mov	eax, s_error_open_obj_file
	jmp	fatal_error
;[cf]
;[of]:error cant read obj
error_cant_read_obj:

	mov	eax, s_error_read_obj_file
	jmp	fatal_error
;[cf]
;[of]:error invalid machine
error_invalid_machine:

	mov	eax, s_error_invalid_machine
	jmp	fatal_error
;[cf]
;[cf]
;[of]:save
;[c]Save the obj file
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj object
;[c]	ebx	the filename
;[c]
obj_save:

	pushad
	mov	esi, eax
	
	locals
	local	.filename,	dword
	local	.file,	file
	local	.buffer,	memory_buffer
	local	.strings,	memory_buffer
	local	.name,	qword
	local	.offset_raw_data,	dword
	endl
	
	call	obj_save_initialize
	call	obj_save_header
	call	obj_save_sections
	call	obj_save_raw_data
	call	obj_save_symbol_table
	call	obj_save_strings
	call	obj_save_file
	call	obj_save_release
	
	mov	esp, ebp
	popad
	ret

;[of]:save initialize
obj_save_initialize:

	mov	[obj_save.filename], ebx
	
	lea	eax, [obj_save.buffer]
	call	memory_buffer_initialize
	lea	eax, [obj_save.strings]
	call	memory_buffer_initialize
	lea	eax, [obj_save.strings]
	sub	ebx, ebx
	call	memory_buffer_add_dword
	
	ret
;[cf]
;[of]:save release
obj_save_release:

	lea	eax, [obj_save.buffer]
	call	memory_buffer_release
	lea	eax, [obj_save.strings]
	call	memory_buffer_release
	
	ret
;[cf]
;[of]:save header
;[c]Writes the header to the buffer and setup some offsets
;[c]
obj_save_header:

	; Write machine type
	mov	ax, [esi + obj.machine]
	call	obj_write_word
	
	; Number of sections
	mov	eax, [esi + obj.num_of_sections]
	call	obj_write_word
	
	; Time stamp
	mov	eax, [esi + obj.time_date_stamp]
	call	obj_write_dword

	; Pointer to symbol table
	mov	eax, sizeof.section_header
	mul	[esi + obj.num_of_sections]
	add	eax, sizeof.obj_header
	add	eax, [esi + obj.optional_header + memory_buffer.used]	
	mov	[obj_save.offset_raw_data], eax	; save offset to raw data
	add	eax, [esi + obj.raw_data + memory_buffer.used]	
	call	obj_write_dword
	
	; Number of symbols
	mov	eax, [esi + obj.num_of_symbols]
	call	obj_write_dword
	
	; Size of optional header
	mov	eax, [esi + obj.optional_header + memory_buffer.used]
	call	obj_write_word
	
	; Characteristics
	mov	ax, [esi + obj.characteristics]
	jmp	obj_write_word
;[cf]
;[of]:save sections
;[c]Save all section headers
;[c]
obj_save_sections:

	mov	ecx, [esi + obj.num_of_sections]
	mov	edi, [esi + obj.section_table + memory_buffer.p]
	jecxz	.done
.loop:	push	ecx
	call	obj_save_section
	pop	ecx
	add	edi, sizeof.obj_section
	loop	.loop
.done:	ret

;[of]:save section
obj_save_section:

	; Write name
	mov	eax, [edi + obj_section.name]
	call	obj_write_inline_name

	; Write virtual size and address
	mov	eax, [edi + obj_section.virtual_size]
	call	obj_write_dword
	mov	eax, [edi + obj_section.virtual_address]
	call	obj_write_dword
	
	; Raw data
	mov	eax, [edi + obj_section.size_of_raw_data]
	call	obj_write_dword
	mov	eax, [edi + obj_section.pointer_to_raw_data]
	call	convert_pointer_to_file_offset
	call	obj_write_dword
	
	mov	eax, [edi + obj_section.pointer_to_relocations]
	call	convert_pointer_to_file_offset
	call	obj_write_dword
	
	mov	eax, [edi + obj_section.pointer_to_line_numbers]
	call	convert_pointer_to_file_offset
	call	obj_write_dword
	
	mov	ax, [edi + obj_section.number_of_relocations]
	call	obj_write_word
	
	mov	ax, [edi + obj_section.number_of_line_numbers]
	call	obj_write_word

	mov	eax, [edi + obj_section.characteristics]
	jmp	obj_write_dword

;[of]:convert pointer to file offset
;[c]Converts a pointer relative to raw data to a pointer relative to the 
;[c]beginning of the file.
;[c]
;[c]ARGUMENTS
;[c]	eax	the value
;[c]	ebp	the locals
;[c]
;[c]RETURN VALUES
;[c]	eax	the file offset
;[c]
convert_pointer_to_file_offset:

	cmp	eax, -1
	jz	.null
	add	eax, [obj_save.offset_raw_data]
	ret
.null:	sub	eax, eax
	ret
;[cf]
;[cf]
;[cf]
;[of]:save raw data
obj_save_raw_data:

	mov	eax, [esi + obj.raw_data + memory_buffer.p]
	mov	ebx, [esi + obj.raw_data + memory_buffer.used]
	jmp	obj_write_bytes
;[cf]
;[of]:save symbol table
obj_save_symbol_table:

	mov	ecx, [esi + obj.num_of_symbols]
	mov	edi, [esi + obj.symbol_table + memory_buffer.p]
	jecxz	.done
.loop:	push	ecx
	call	obj_save_symbol
	pop	ecx
	add	edi, sizeof.obj_symbol
	loop	.loop
.done:	ret

;[of]:save symbol
obj_save_symbol:

	mov	eax, [edi + obj_symbol.name]
	call	write_symbol_name
	mov	eax, [edi + obj_symbol.value]
	call	obj_write_dword
	mov	ax, [edi + obj_symbol.section_number]
	call	obj_write_word
	mov	ax, [edi + obj_symbol.type]
	call	obj_write_word
	mov	al, [edi + obj_symbol.storage_class]
	call	obj_write_byte
	mov	al, [edi + obj_symbol.number_of_aux_symbols]
	jmp	obj_write_byte

;[of]:write symbol name
write_symbol_name:

	push	eax
	call	string_get_size
	mov	ecx, eax
	cmp	ecx, 9
	pop	eax
	jnc	.long_name
	jmp	obj_write_inline_name

.long_name:

	push	eax
	push	ecx
	sub	eax, eax
	call	obj_write_dword
	mov	eax, [obj_save.strings + memory_buffer.used]
	call	obj_write_dword
	pop	ecx
	inc	ecx
	pop	ebx
	lea	eax, [obj_save.strings]
	jmp	memory_buffer_add_bytes
;[cf]
;[cf]
;[cf]
;[of]:save strings
obj_save_strings:

	mov	eax, [obj_save.strings + memory_buffer.p]
	mov	ebx, [obj_save.strings + memory_buffer.used]
	mov	[eax], ebx
	jmp	obj_write_bytes
;[cf]
;[of]:save file
;[c]Save the obj file
;[c]
;[c]ARGUMENTS
;[c]	esi	the obj object
;[c]
obj_save_file:

	; Open
	lea	eax, [obj_save.file]
	mov	ebx, [obj_save.filename]
	call	file_open_write
	jnz	error_cant_open_obj

	; Write
	lea	eax, [obj_save.file]
	mov	ebx, [obj_save.buffer + memory_buffer.p]
	mov	ecx, [obj_save.buffer + memory_buffer.used]
	call	file_write
	jnz	error_cant_write_obj
	
	; Close
	lea	eax, [obj_save.file]
	jmp	file_close
;[cf]

;[of]:write byte
obj_write_byte:

	mov	ebx, eax
	lea	eax, [obj_save.buffer]
	jmp	memory_buffer_add_byte
;[cf]
;[of]:write word
obj_write_word:

	mov	ebx, eax
	lea	eax, [obj_save.buffer]
	jmp	memory_buffer_add_word
;[cf]
;[of]:write dword
obj_write_dword:

	mov	ebx, eax
	lea	eax, [obj_save.buffer]
	jmp	memory_buffer_add_dword
;[cf]
;[of]:write bytes
obj_write_bytes:

	mov	ecx, ebx
	mov	ebx, eax
	lea	eax, [obj_save.buffer]
	jmp	memory_buffer_add_bytes
;[cf]
;[of]:write inline name
;[c]Write a short name inisde the 8 bytes
;[c]
;[c]ARGUMENTS
;[c]	eax	the name
;[c]	ebp	locals
;[c]
obj_write_inline_name:

	push	edi
	mov	ebx, eax
	lea	edi, [obj_save.name]
	mov	ecx, 8
	jmp	.1
.next:	stosb
	inc	ebx
	dec	ecx
.1:	mov	al, [ebx]
	or	al, al
	jnz	.next
	sub	al, al
	rep	stosb
	pop	edi
	lea	eax, [obj_save.name]
	mov	ebx, 8
	jmp	obj_write_bytes
;[cf]

;[of]:error cant write obj
error_cant_write_obj:

	mov	eax, s_error_write_obj_file
	jmp	fatal_error
;[cf]
;[cf]
;[cf]
;[of]:adding - removing
;[of]:add section
;[c]Add a new section
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj
;[c]	ebx	the name of the section
;[c]	ecx	the flags
;[c]
;[c]RETURN VALUES
;[c]	eax	the section index
;[c]
obj_add_section:

	push	esi
	push	edi
	mov	esi, eax
	mov	eax, [esi + obj.num_of_sections]
	push	eax
	inc	[esi + obj.num_of_sections]
	
	; Reserve space for the section
	push	ebx
	push	ecx
	lea	eax, [esi + obj.section_table]
	mov	ebx, [esi + obj.section_table + memory_buffer.used]
	mov	ecx, sizeof.obj_section
	call	memory_buffer_make_room
	mov	edi, eax
	pop	ecx
	pop	ebx
	
	; Initialize the section
	sub	eax, eax
	mov	[edi + obj_section.name], ebx
	mov	[edi + obj_section.virtual_size], eax
	mov	[edi + obj_section.virtual_address], eax
	mov	[edi + obj_section.size_of_raw_data], eax
	mov	[edi + obj_section.pointer_to_raw_data], -1
	mov	[edi + obj_section.pointer_to_relocations], -1
	mov	[edi + obj_section.pointer_to_line_numbers], -1
	mov	[edi + obj_section.number_of_relocations], ax
	mov	[edi + obj_section.number_of_line_numbers], ax
	mov	[edi + obj_section.characteristics], ecx
	
	pop	eax
	inc	eax
	pop	edi
	pop	esi
	ret
;[cf]
;[of]:add symbol
;[c]Add a new symbol
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj
;[c]	ebx	the name of the symbol
;[c]
;[c]RETURN VALUES
;[c]	eax	the symbol index
;[c]	ebx	the symbol address
;[c]
obj_add_symbol:

	push	esi
	push	edi
	mov	esi, eax
	mov	eax, [esi + obj.num_of_symbols]
	push	eax
	inc	[esi + obj.num_of_symbols]
	
	; Reserve space for the symbol
	push	ebx
	lea	eax, [esi + obj.symbol_table]
	mov	ebx, [esi + obj.symbol_table + memory_buffer.used]
	mov	ecx, sizeof.obj_symbol
	call	memory_buffer_make_room
	mov	edi, eax
	pop	ebx
	
	; Initialize the symbol
	sub	eax, eax
	mov	[edi + obj_symbol.name], ebx
	mov	[edi + obj_symbol.value], eax
	mov	[edi + obj_symbol.section_number], ax
	mov	[edi + obj_symbol.type], ax
	mov	[edi + obj_symbol.storage_class], al
	mov	[edi + obj_symbol.number_of_aux_symbols], al

	; Register it
	lea	eax, [esi + obj.symbol_list]
	mov	ecx, edi
	call	dictionary_add
	
	mov	eax, edi
	pop	ebx
	pop	edi
	pop	esi
	ret
;[cf]

;[of]:section add raw data
;[c]Add raw data to a section
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj
;[c]	ebx	the section id
;[c]	ecx	the raw data
;[c]	edx	size of raw data
;[c]
obj_section_add_raw_data:

	push	esi
	mov	esi, eax
	
	push	ecx
	push	edx
	call	obj_get_section_at_index
	pop	ecx
	pop	ebx
	mov	edi, eax
	mov	eax, [esi + obj.raw_data + memory_buffer.used]
	mov	[edi + obj_section.pointer_to_raw_data], eax
	mov	[edi + obj_section.size_of_raw_data], ecx
	
	lea	eax, [esi + obj.raw_data]
	call	memory_buffer_add_bytes
	
	pop	esi
	ret
;[cf]
;[of]:section add relocations
;[c]Add relocations to a section
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj
;[c]	ebx	the section id
;[c]	ecx	the relocations
;[c]	edx	the number of relocations
;[c]
obj_section_add_relocations:

	push	esi
	mov	esi, eax
	
	push	ecx
	push	edx
	call	obj_get_section_at_index
	pop	ecx
	pop	ebx
	mov	edi, eax
	mov	eax, [esi + obj.raw_data + memory_buffer.used]
	mov	[edi + obj_section.pointer_to_relocations], eax
	mov	[edi + obj_section.number_of_relocations], cx
	
	mov	eax, sizeof.relocation_item
	mul	ecx
	mov	ecx, eax
	lea	eax, [esi + obj.raw_data]
	call	memory_buffer_add_bytes
	
	pop	esi
	ret
;[cf]
;[cf]
;[of]:accessing
;[of]:get symbol at index
;[c]Get the symbol at index
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj
;[c]	ebx	the index
;[c]
;[c]RETURN VALUES
;[c]	eax	the obj_symbol
;[c]
obj_get_symbol_at_index:

	xchg	eax, ebx
	dec	eax
	mov	ecx, sizeof.obj_symbol
	mul	ecx
	add	eax, [ebx + obj.symbol_table + memory_buffer.p]
	ret

;[cf]
;[of]:get section at index
;[c]Get the section at index
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj
;[c]	ebx	the section index [1, ... [
;[c]
;[c]RETURN VALUES
;[c]	eax	the obj_section
;[c]
obj_get_section_at_index:

	xchg	eax, ebx
	dec	eax
	mov	ecx, sizeof.obj_section
	mul	ecx
	add	eax, [ebx + obj.section_table + memory_buffer.p]
	ret

;[cf]
;[cf]
;[of]:searching
;[of]:find section
;[c]Finds the first section with given name
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj
;[c]	ebx	the section name
;[c]
;[c]RETURN VALUES
;[c]	eax	the section
;[c]	zf	1 if found
;[c]
obj_find_section:

	push	esi
	push	edi
	mov	esi, eax
	mov	edi, ebx
	
	mov	ecx, [esi + obj.num_of_sections]
	mov	esi, [esi + obj.section_table + memory_buffer.p]
	jecxz	.2
.1:	push	ecx
	mov	eax, [esi + obj_section.name]
	mov	ebx, edi
	call	string_compare
	pop	ecx
	jz	.found
	add	esi, sizeof.obj_section
	loop	.1
.2:	or	esi, esi	; force zf = 0
.found:	mov	eax, esi
	pop	edi
	pop	esi
	ret
;[cf]
;[of]:find symbol
;[c]Find a symbol by name
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj object
;[c]	ebx	the symbol name
;[c]
;[c]RETURN VALUES
;[c]	eax	the symbol
;[c]	ebx	the symbol index
;[c]	zf	1 if not found
;[c]
obj_find_symbol:

	push	esi
	mov	esi, eax
	
	lea	eax, [esi + obj.symbol_list]
	call	dictionary_get_value
	jz	.not_found
	mov	ebx, eax
	add	ebx, [esi + obj.symbol_table + memory_buffer.p]	; convert offset to pointer
	
	sub	edx, edx
	mov	ecx, sizeof.obj_symbol
	div	ecx
	
	xchg	eax, ebx
	or	eax, eax	; force zf = 0
	
.not_found:
	
	pop	esi
	ret
;[cf]
;[cf]
;[of]:private
;[of]:read obj symbol name
;[c]
;[c]ARGUMENTS
;[c]	eax	the address of the name limited to 8 bytes 
;[c]
;[c]RETURN VALUES
;[c]	eax	the string
;[c]
;[c]REMARKS
;[c]	edx saved.
;[c]
read_obj_symbol_name:

	push	edx
	lea	ecx, [eax + 8]
	mov	ebx, eax
.1:	cmp	ebx, ecx
	jz	.2
	cmp	byte [ebx], 0
	jz	.2
	inc	ebx
	jmp	.1
.2:	sub	ebx, eax
	call	string_copy_range
	pop	edx
	ret
;[cf]
;[cf]
;[cf]
;[of]:cv8
;[of]:definitions
	struct	cv8
	attr	raw,	memory_buffer	
	attr	relocations,	memory_buffer	
	attr	num_of_relocations,	dword	
	attr	fas,	dword	
	attr	obj,	dword	
	attr	files,	dword	
	attr	sections,	identity_dictionary	; the list of all sections
				
	attr	start,	dword	; start offset of the current section, used to compute and write size.
	attr	recstart,	dword	; start offset of the current record (after reclen and rectyp)
	ends			
				
	struct	cv8_section		
	attr	section_index,	dword	; the section id
	attr	obj_section,	dword	; the associated obj section
	attr	rows,	sorted_vector	; list of all rows for this section
	ends			
				
	struct	cv8_file_info		
	attr	name,	dword	
	attr	name_offset,	dword	; offset of the name in the filename list (F3)
	attr	file_offset,	dword	; offset of the file item
	ends			
				
	struct	file_item		
	attr	offset,	dword	; offset in F3 table
	attr	type,	word	; $110
	attr	md5,	byte, 16	; MD5 signature
	attr	pad,	word	
	ends			
				
	struct	line_info		
	attr	start_offset,	dword	; start offset in section (SECREL to section start)
	attr	section_index,	word	; index (SECTION to section start)
	attr	pad,	word	; pad/align (0)
	attr	section_length,	dword	; section length covered by line number info
	ends
;[cf]
;[of]:initialize - release
;[of]:initialize
;[c]Initialize the cv8 object
;[c]
;[c]ARGUMENTS
;[c]	eax	the cv8 object
;[c]	ebx	the fas object
;[c]	ecx	the obj object
;[c]
cv8_initialize:

	push	esi
	mov	esi, eax
	mov	[esi + cv8.fas], ebx
	mov	[esi + cv8.obj], ecx
	mov	[esi + cv8.files], 0
	mov	[esi + cv8.num_of_relocations], 0
	
	lea	eax, [esi + cv8.raw]
	call	memory_buffer_initialize
	lea	eax, [esi + cv8.relocations]
	call	memory_buffer_initialize
	lea	eax, [esi + cv8.sections]
	call	identity_dictionary_initialize
	
	pop	esi
	ret
;[cf]
;[of]:release
;[c]Release the cv8 object
;[c]
;[c]ARGUMENTS
;[c]	eax	the cv8 object
;[c]
cv8_release:

	push	esi
	mov	esi, eax
	
	lea	eax, [esi + cv8.raw]
	call	memory_buffer_release
	lea	eax, [esi + cv8.relocations]
	call	memory_buffer_release
	
	lea	eax, [esi + cv8.sections]
	mov	ecx, cv8_section_delete
	call	dictionary_each_value_and_release
	
	mov	eax, [esi + cv8.files]
	call	free_memory_with_size

	pop	esi
	ret
;[cf]
;[cf]
;[of]:generating
;[of]:write debug s
;[c]Generates the .debug$S raw data
;[c]
;[c]ARGUMENTS
;[c]	eax	the cv8 object
;[c]
cv8_write_debug_s:

	pushad
	mov	esi, eax

	; Write debug signature
	mov	eax, 4
	call	cv8_write_dword
	
	call	cv8_write_string_table
	call	cv8_write_file_checksums
	call	cv8_write_lines
	call	cv8_write_symbols
	
	popad
	ret

;[of]:write string table
;[c]Fill the string table. Also create a list to keep track of the offsets
;[c](used when creating the checksum table).
;[c]
cv8_write_string_table:

	; Allocate buffer to store file info
	mov	eax, [esi + cv8.fas]
	mov	eax, [eax + fas.files + dictionary.tally]
	mov	ebx, sizeof.cv8_file_info
	mul	ebx
	call	get_memory_with_size
	mov	[esi + cv8.files], eax

	mov	eax, DEBUG_S_STRINGTABLE
	call	cv8_open_section

	; edi - to compute offsets in the table
	mov	edi, [esi + cv8.raw + memory_buffer.used]	

	sub	eax, eax
	call	cv8_write_byte

	mov	eax, [esi + cv8.fas]
	lea	eax, [eax + fas.files]
	mov	ecx, .file
	mov	ebp, [esi + cv8.files]
	call	dictionary_each_value
	
	jmp	cv8_close_section
	
;[c]
.file:	mov	ebx, [esi + cv8.raw + memory_buffer.used]
	sub	ebx, edi
	mov	[ebp + cv8_file_info.name], eax
	mov	[ebp + cv8_file_info.name_offset], ebx
	add	ebp, sizeof.cv8_file_info
	
	mov	ebx, eax
	jmp	cv8_write_string
;[cf]
;[of]:write file checksums
cv8_write_file_checksums:

	push	ebp
	
	locals
	local	.source_file,	file
	local	.source_buffer,	memory_buffer
	local	.md5,	md5digest
	endl
	
	lea	eax, [.source_buffer]
	call	memory_buffer_initialize
	
	mov	eax, DEBUG_S_FILECHKSMS
	call	cv8_open_section

	mov	edi, [esi + cv8.files]
	mov	eax, [esi + cv8.fas]
	mov	ecx, [eax + fas.files + dictionary.tally]
	sub	edx, edx
.loop:	push	ecx
	push	edx
	
	; Save the position of file info
	mov	[edi + cv8_file_info.file_offset], edx
	
	mov	eax, [edi + cv8_file_info.name_offset]	; name of the file
	call	cv8_write_dword	;
			
	mov	al, 16	; hash length (16 bytes for MD5)
	call	cv8_write_byte	;
			
	mov	al, 1	; hash type (MD5 = 1)
	call	cv8_write_byte	;
			
	mov	eax, [edi + cv8_file_info.name]	; md5 hash
	call	get_file_md5	;
	mov	ebx, 16	;
	call	cv8_write_bytes	;
			
	sub	eax, eax	; padding
	call	cv8_write_word	;
	
	pop	edx
	pop	ecx
	add	edi, sizeof.cv8_file_info
	add	edx, sizeof.file_item
	loop	.loop
	
	lea	eax, [.source_buffer]
	call	memory_buffer_release

	call	cv8_close_section
	
	mov	esp, ebp
	pop	ebp
	ret

;[of]:get file md5
;[c]Return the MD5 of a file
;[c]
;[c]ARGUMENTS
;[c]	eax	the filename
;[c]
;[c]RETURN VALUES
;[c]	eax	the address of the MD5
;[c]
;[c]REMARKS
;[c]	If an error occurs while reading a file, the MD5 is empty.
;[c]
get_file_md5:

	push	eax
	lea	eax, [cv8_write_file_checksums.source_buffer]
	call	memory_buffer_remove_all
	pop	eax
	
	mov	ebx, eax
	lea	eax, [cv8_write_file_checksums.source_file]
	call	file_open_read
	jnz	.no_md5_1
	lea	eax, [cv8_write_file_checksums.source_file]
	lea	ebx, [cv8_write_file_checksums.source_buffer]
	call	file_read_all
	jnz	.no_md5_2
	lea	eax, [cv8_write_file_checksums.source_file]
	call	file_close

	lea	eax, [cv8_write_file_checksums.source_buffer]
	lea	ebx, [cv8_write_file_checksums.md5]
	call	memory_buffer_get_md5
	
	lea	eax, [cv8_write_file_checksums.md5]
	ret

.no_md5_2:

	lea	eax, [cv8_write_file_checksums.source_file]
	call	file_close

.no_md5_1:

	mov	eax, .zeroes
	ret

.zeroes	dd	0, 0, 0, 0
;[cf]

;[cf]
;[of]:write lines
;[c]Creates one F2 section for each section of the fas file
;[c]
cv8_write_lines:

	call	cv8_build_sections
		
	lea	eax, [esi + cv8.sections]
	mov	ecx, cv8_make_section
	jmp	dictionary_each_value

;[of]:build sections
;[c]Group rows by sections
;[c]
cv8_build_sections:

	push	ebp
	
	locals
	local	.current_section_index,	dword
	local	.current_section,	dword
	endl
	
	mov	[.current_section_index], -1
	
	mov	eax, [esi + cv8.fas]
	mov	ecx, [eax + fas.num_of_rows]
	mov	edi, [eax + fas.rows]
.loop:	push	ecx
;[c]	
;[c]	Find or create the section object for this row
;[c]	
	mov	eax, [edi + fas_row_dump.section]
	cmp	eax, [.current_section_index]
	jz	.1
	mov	[.current_section_index], eax
	
	; Find existing section
	mov	ebx, eax
	lea	eax, [esi + cv8.sections]
	call	dictionary_get_value
	jnz	.set
	
	; Create a new section
	push	edi
	mov	eax, [.current_section_index]
	call	cv8_section_new
	mov	edi, eax
	
	; Associate the section object from the obj file
	mov	ebx, [.current_section_index]
	mov	eax, [esi + cv8.obj]
	call	obj_get_section_at_index
	mov	[edi + cv8_section.obj_section], eax
	
	; Add it to the list
	mov	ecx, edi
	mov	ebx, [.current_section_index]
	lea	eax, [esi + cv8.sections]
	call	dictionary_add
	
	mov	eax, edi
	pop	edi
	
.set:	mov	[.current_section], eax
.1:	
;[c]	
;[c]	Add the row to this section
;[c]	
	mov	eax, [.current_section]
	lea	eax, [eax + cv8_section.rows]
	mov	ebx, edi
	call	sorted_vector_add
;[c]	
	pop	ecx
	add	edi, sizeof.fas_row_dump
	loop	.loop
	
	mov	esp, ebp
	pop	ebp
	ret
;[cf]
;[of]:make section
;[c]Process one section
;[c]
;[c]ARGUMENTS
;[c]	eax	the section (cv8_section)
;[c]	esi	the cv8 object
;[c]
cv8_make_section:

	push	ebp
	
	locals
	local	.current_section,	dword
	local	.offset,	dword
	local	.reloc_start,	dword
	local	.num_of_files,	dword
			
	local	.file_header_start,	dword
	local	.current_filename,	dword
	local	.num_of_pairs,	dword
	endl
	
	mov	[.current_section], eax
	mov	ebx, [eax + cv8_section.obj_section]
	mov	ebx, [ebx + obj_section.output_offset]
	mov	[.offset], ebx
	mov	[.current_filename], 0
	mov	[.num_of_files], 0
	
	; Start a section segment
	call	open_obj_section
	
	; Process each row
	mov	eax, [.current_section]
	lea	eax, [eax + cv8_section.rows]
	mov	ecx, cv8_make_row
	call	vector_each
	
	; Close the last file header
	call	close_file_header
	
	; Close the section segment
	call	close_obj_section
	
	mov	esp, ebp
	pop	ebp
	ret

;[of]:open obj section
;[c]
;[c]ARGUMENTS
;[c]	eax	the cv8 section
;[c]	esi	the cv8
;[c]
open_obj_section:

	push	edi
	mov	edi, eax
	
	mov	eax, DEBUG_S_LINES
	call	cv8_open_section
	
	mov	eax, [esi + cv8.raw + memory_buffer.used]
	mov	[cv8_make_section.reloc_start], eax
	
	; Write line info
	sub	eax, eax	; start offset in section
	call	cv8_write_dword	;
	sub	eax, eax	; section index
	call	cv8_write_word	;
	sub	eax, eax	; padding
	call	cv8_write_word	;
	mov	eax, [edi + cv8_section.obj_section]	;size
	mov	eax, [eax + obj_section.size_of_raw_data]	;
	call	cv8_write_dword	;
	
	; Write relocation info for line
	mov	eax, [cv8_make_section.reloc_start]
	mov	ebx, [edi + cv8_section.obj_section]
	mov	ebx, [ebx + obj_section.symbol_index]
	call	cv8_write_symbol_relocation
	
	pop	edi
	ret
;[cf]
;[of]:close obj section
close_obj_section:

	jmp	cv8_close_section
;[cf]
;[of]:make row
cv8_make_row:

	mov	edi, eax
	
	; Same file ?
	mov	eax, [edi + fas_row_dump.filename]
	cmp	eax, [cv8_make_section.current_filename]
	jz	.same_file
	
	call	close_file_header
	mov	eax, [edi + fas_row_dump.filename]
	call	open_file_header
	
.same_file:
	
	; Increment pair counter
	inc	[cv8_make_section.num_of_pairs]
	
	; Write pair
	mov	eax, [edi + fas_row_dump.offset_output]
	sub	eax, [cv8_make_section.offset]
	call	cv8_write_dword
	mov	eax, [edi + fas_row_dump.line_number]
	or	eax, $80000000
	jmp	cv8_write_dword
;[cf]
;[of]:open file header
;[c]Open a file header: writes (partial) information on current file
;[c]
;[c]ARGUMENTS
;[c]	esi	the cv8 object
;[c]	eax	the filename
;[c]	ebp	the locals
;[c]
open_file_header:

	inc	[cv8_make_section.num_of_files]
	
	mov	[cv8_make_section.current_filename], eax
	mov	[cv8_make_section.num_of_pairs], 0
	
	; Save the position of this file header to complete it later
	; (the number of pairs is not known yet)
	mov	ebx, [esi + cv8.raw + memory_buffer.used]
	mov	[cv8_make_section.file_header_start], ebx
	
	mov	ebx, eax	; pointer to file info
	mov	eax, esi	;
	call	cv8_get_file_info	;
	mov	eax, [eax + cv8_file_info.file_offset]	;
	call	cv8_write_dword	;
	sub	eax, eax	; number of pairs
	call	cv8_write_dword	;
	sub	eax, eax	; size of segment
	call	cv8_write_dword	;

	ret
;[cf]
;[of]:close file header
close_file_header:

	mov	eax, [cv8_make_section.current_filename]	
	or	eax, eax	; nothing to flush
	jz	.done	;

	mov	eax, [esi + cv8.raw + memory_buffer.p]
	add	eax, [cv8_make_section.file_header_start]
	mov	ebx, [cv8_make_section.num_of_pairs]
	mov	[eax + 4], ebx
	shl	ebx, 3
	add	ebx, 12
	mov	[eax + 8], ebx

.done:	ret
;[cf]
;[cf]
;[cf]
;[of]:write symbols
cv8_write_symbols:

	mov	eax, DEBUG_S_SYMBOLS
	call	cv8_open_section
	
	call	cv8_write_object_name
	call	cv8_write_creator
	call	cv8_add_symbols
	
	jmp	cv8_close_section

;[of]:write object name
cv8_write_object_name:

	mov	ax, S_OBJNAME	; type
	call	cv8_open_record	;
			
	sub	eax, eax	; language (asm)
	call	cv8_write_dword	;
			
	mov	eax, [dbg_name]	; object name - use of global :-(
	call	cv8_write_string	;
			
	jmp	cv8_close_record
;[cf]
;[of]:write creator
cv8_write_creator:

	mov	ax, S_COMPILE2	; type
	call	cv8_open_record	;
			
	mov	eax, 3	; flags
	call	cv8_write_dword	;
			
	mov	eax, 3	; machine
	call	cv8_write_word	;
			
	sub	eax, eax	; ... (cf. compile_sym)
	call	cv8_write_word	;
	sub	eax, eax	;
	call	cv8_write_word	;
	sub	eax, eax	;
	call	cv8_write_word	;
			
	sub	eax, eax	;
	call	cv8_write_word	;
	sub	eax, eax	;
	call	cv8_write_word	;
	sub	eax, eax	;
	call	cv8_write_word	;
			
	mov	eax, s_fasm	; creator name
	call	cv8_write_string	;
			
	sub	eax, eax	;
	call	cv8_write_word	;
			
	jmp	cv8_close_record	
;[cf]
;[of]:add symbols
;[c]
;[c]ARGUMENTS
;[c]	esi	the cv8 object
;[c]
cv8_add_symbols:

	mov	eax, [esi + cv8.fas]
	mov	ecx, .symbol
	jmp	fas_each_symbol

.symbol:

	; Ignore external symbols
	cmp	[eax + fas_symbol.usable], 0
	jz	.done
	
	mov	edi, eax
	
	; Is there already a public symbol in the object file ?
	mov	eax, [esi + cv8.obj]
	mov	ebx, [edi + fas_symbol.name]
	call	obj_find_symbol
	jnz	.found
	
	; Not found: create a new symbol
	mov	eax, [esi + cv8.obj]
	mov	ebx, [edi + fas_symbol.name]
	call	obj_add_symbol
	
	mov	[eax + obj_symbol.storage_class], 2
	mov	ecx, [edi + fas_symbol.section]
	mov	edx, dword [edi + fas_symbol.value]
	mov	[eax + obj_symbol.section_number], cx
	mov	[eax + obj_symbol.value], edx
		
.found:	cmp	[edi + fas_symbol.size_data], 0
	jnz	cv8_add_data
	jmp	cv8_add_label

.done:	ret

;[of]:add label
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj_symbol
;[c]	ebx	the symbol index
;[c]	esi	the cv8 object
;[c]	ebp	the locals
;[c]
cv8_add_label:

	push	edi
	mov	edi, eax
	push	ebx
	
	mov	ax, S_LABEL32	; open the label_sym_32
	call	cv8_open_record	;
	sub	eax, eax	; off
	call	cv8_write_dword	;
	sub	eax, eax	; seg
	call	cv8_write_word	;
	sub	al, al	; flags
	call	cv8_write_byte	;
	mov	eax, [edi + obj_symbol.name]	; name
	call	cv8_write_string	;
	call	cv8_close_record
	
	mov	eax, [esi + cv8.recstart]
	pop	ebx
	call	cv8_write_symbol_relocation

	pop	edi
	ret
;[cf]
;[of]:add data
;[c]
;[c]ARGUMENTS
;[c]	eax	the obj_symbol
;[c]	ebx	the symbol index
;[c]	esi	the cv8 object
;[c]	edi	the fas_symbol
;[c]	ebp	the locals
;[c]
cv8_add_data:

	push	ebx
	push	eax
	
	mov	ax, S_LDATA32	; open the datas_sym_32
	call	cv8_open_record	;
	mov	ebx, [edi + fas_symbol.size_data]	; type
	mov	eax, .types	;
	call	lookup	;
	call	cv8_write_dword	;
	sub	eax, eax	; off
	call	cv8_write_dword	;
	sub	eax, eax	; seg
	call	cv8_write_word	;
	pop	eax	; name
	mov	eax, [eax + obj_symbol.name]	;
	call	cv8_write_string	;
	call	cv8_close_record
	
	mov	eax, [esi + cv8.recstart]
	add	eax, 4	; skip 'type'
	pop	ebx
	jmp	cv8_write_symbol_relocation

.types	dd	1,	T_UCHAR
	dd	2,	T_USHORT
	dd	4,	T_UINT4
	dd	8,	T_UQUAD
	dd	0,	T_UCHAR	; the default
;[cf]
;[cf]
;[cf]
;[cf]
;[cf]
;[of]:private
;[of]:write byte
cv8_write_byte:

	mov	ebx, eax
	lea	eax, [esi + cv8.raw]
	jmp	memory_buffer_add_byte
;[cf]
;[of]:write word
cv8_write_word:

	mov	ebx, eax
	lea	eax, [esi + cv8.raw]
	jmp	memory_buffer_add_word
;[cf]
;[of]:write dword
cv8_write_dword:

	mov	ebx, eax
	lea	eax, [esi + cv8.raw]
	jmp	memory_buffer_add_dword
;[cf]
;[of]:write bytes
cv8_write_bytes:

	mov	ecx, ebx
	mov	ebx, eax
	lea	eax, [esi + cv8.raw]
	jmp	memory_buffer_add_bytes
;[cf]
;[of]:write string
cv8_write_string:

	push	eax
	call	string_get_size
	mov	ecx, eax
	inc	ecx
	pop	ebx
	lea	eax, [esi + cv8.raw]
	jmp	memory_buffer_add_bytes
;[cf]
;[of]:write symbol relocation
;[c]
;[c]ARGUMENTS
;[c]	esi	the cv8 object
;[c]	eax	the offset of the 6 byte data to fix up (off + seg)
;[c]	ebx	the symbol index
;[c]	
cv8_write_symbol_relocation:

	; Write relocation info for line
	push	eax
	push	ebx
	mov	ecx, I386_SECREL
	call	cv8_write_relocation
	pop	ebx
	pop	eax

	add	eax, 4
	mov	ecx, I386_SECTION
	jmp	cv8_write_relocation
;[cf]
;[of]:write relocation
cv8_write_relocation:

	inc	[esi + cv8.num_of_relocations]

	push	ecx
	push	ebx
	mov	ebx, eax
	lea	eax, [esi + cv8.relocations]
	call	memory_buffer_add_dword
	pop	ebx
	lea	eax, [esi + cv8.relocations]
	call	memory_buffer_add_dword
	pop	ebx
	lea	eax, [esi + cv8.relocations]
	jmp	memory_buffer_add_word
;[cf]
;[of]:align 4
cv8_align_4:

	mov	ecx, [esi + cv8.raw + memory_buffer.used]
	and	ecx, 3
	neg	ecx
	add	ecx, 4
	and	ecx, 3

	mov	ebx, .blanks
	lea	eax, [esi + cv8.raw]
	jmp	memory_buffer_add_bytes

.blanks	db	0, 0, 0
;[cf]

;[of]:section new
;[c]Create a new section object
;[c]
;[c]ARGUMENTS
;[c]	eax	the section id
;[c]
;[c]RETURN VALUES
;[c]	eax	the section
;[c]
cv8_section_new:

	push	eax
	mov	eax, sizeof.cv8_section
	call	get_memory
	pop	ebx
	
	push	eax
	mov	[eax + cv8_section.section_index], ebx
	lea	eax, [eax + cv8_section.rows]
	mov	ebx, .compare
	call	sorted_vector_initialize
	pop	eax
	ret

.compare:
	
	mov	ecx, [eax + fas_row_dump.offset_output]
	cmp	ecx, [ebx + fas_row_dump.offset_output]
	ret
;[cf]
;[of]:section delete
cv8_section_delete:

	push	eax
	lea	eax, [eax + cv8_section.rows]
	call	sorted_vector_release
	pop	eax
	mov	ebx, sizeof.cv8_section
	jmp	free_memory
;[cf]

;[of]:get file info
;[c]Returns the file info for the given filename
;[c]
;[c]ARGUMENTS
;[c]	eax	the cv8 object
;[c]	ebx	the filename
;[c]
;[c]RETURN VALUES
;[c]	eax	the file info
;[c]
cv8_get_file_info:

	mov	ecx, [eax + cv8.fas]
	mov	ecx, [ecx + fas.files + dictionary.tally]
	mov	eax, [eax + cv8.files]
.loop:	cmp	ebx, [eax + cv8_file_info.name]
	jz	.found
	add	eax, sizeof.cv8_file_info
	loop	.loop
	
	; There's something wrong
	int	3

.found:	ret
;[cf]

;[of]:open section
;[c]Open a section
;[c]
;[c]ARGUMENTS
;[c]	esi	the cv8 object
;[c]	eax	the type
;[c]
cv8_open_section:

	; Write section header
	call	cv8_write_dword
	sub	eax, eax
	call	cv8_write_dword
	
	; Save the start position (to save length later)
	mov	eax, [esi + cv8.raw + memory_buffer.used]
	mov	[esi + cv8.start], eax
	ret
;[cf]
;[of]:close section
;[c]Close a section
;[c]
;[c]ARGUMENTS
;[c]	esi	the cv8 object
;[c]
cv8_close_section:

	; Adjust field length
	mov	eax, [esi + cv8.raw + memory_buffer.p]
	mov	ebx, [esi + cv8.raw + memory_buffer.used]
	mov	ecx, [esi + cv8.start]
	sub	ebx, ecx
	mov	[eax + ecx - 4], ebx

	jmp	cv8_align_4

;[cf]
;[of]:open record
;[c]Open a subsection
;[c]
;[c]ARGUMENTS
;[c]	esi	the cv8 object
;[c]	eax	the rectyp
;[c]
cv8_open_record:

	push	eax
	sub	eax, eax
	call	cv8_write_word
	pop	eax
	call	cv8_write_word
	
	mov	eax, [esi + cv8.raw + memory_buffer.used]
	mov	[esi + cv8.recstart], eax
	ret
;[cf]
;[of]:close record
cv8_close_record:

	mov	eax, [esi + cv8.raw + memory_buffer.used]
	mov	ebx, [esi + cv8.raw + memory_buffer.p]
	mov	ecx, [esi + cv8.recstart]
	sub	eax, ecx
	add	eax, 2
	mov	[ebx + ecx - 4], ax
	ret
;[cf]
;[cf]
;[cf]
;[of]:data

s_usage	db	13, 10
	db	"Usage: fas2obj fas-name obj-name [debug-obj-name]", 13, 10
	db	"Generate an object file with debug info from a Fasm '.fas' file and its '.obj' file.", 13, 10
	db	"Example: fas2obj hello.fas hello.obj hello_d.obj", 13, 10
	db	13, 10
	db	"If debug-obj-name is not specified, the source obj file is overridden by ", 13, 10
	db	"the debug version.", 13, 10
	db	0
	
s_fasm	db	"fasm", 0
s_debug_s	db	".debug$S", 0
s_debug_t	db	".debug$T", 0
	
s_error_prefix	db	"fas2obj: ", 0
s_error_memory	db	"out of memory", 0
s_error_open_input_file	db	"can't open input file", 0
s_error_read_input_file	db	"error while loading input file", 0
s_error_invalid_fas	db	"input is not a fas file", 0
s_error_open_obj_file	db	"can't open object file", 0
s_error_read_obj_file	db	"error while loading object file", 0
s_error_invalid_machine	db	"unsupported machine type", 0
s_error_debug_found	db	"the object file already has debug info", 0
s_error_write_obj_file	db	"error while saving object file", 0
s_error_too_many_arguments	db	"too many arguments", 0
s_error_no_fas	db	"no fas file specified", 0
s_error_no_obj	db	"no obj file specified", 0
s_error_invalid_option	db	"invalid option", 0
	
; Default debug$T raw data
default_debug_t	dd	$0004
	dw	$0006
	db	$00, $0E, $00, $00, $00, $F2, $F1
sizeof_default_debug_t	=	$ - default_debug_t
;[cf]
;[cf]
