;This class implements a 2D array of arbitrary-sized elements.
;This is accomplished by deriving from the CArrayManager class.
include CArrayManager.inc
;New array instances should be initialized via SetArraySize before being used.
;This can take some time if the array is large.
;Methods are provided for accessing array elements via their 2D coordinate.

class C2DArray, CArrayManager, C++ compatible
virtual SetArraySize:dwWidth,dwHeight,iElementSize
virtual GetPtrXY:_X,_Y ;returns ptr to Element, or NULL=out of array bounds
virtual SetXY:_X,_Y, pElement ;shoves pElement's content into arrayelement [X,Y]
long dwWidth
long dwHeight
endclass

C2DArray_C2DArray proc
ret
C2DArray_C2DArray endp

C2DArray_$C2DArray proc
ret
C2DArray_$C2DArray endp

;This proc not only initializes the internal array size variables,
;it also pushes Width*Height objects of ElementSize into internal buffer.
C2DArray_SetArraySize proc dwWidth,dwHeight,dwElementSize
local pmem
local me
mov me,ecx
m2m [ecx].C2DArray.dwWidth, dwWidth
m2m [ecx].C2DArray.dwHeight, dwHeight
m2m [ecx].C2DArray.dwElementSize, dwElementSize
mov pmem, malloc (dwElementSize)
xor ecx,ecx
.while ecx<dwHeight
push ecx
xor ecx,ecx
.while ecx<dwWidth
push ecx
icall me, C2DArray, push_back, pmem
pop ecx
inc ecx
.endw
pop ecx
inc ecx
.endw
free pmem
ret
C2DArray_SetArraySize endp

C2DArray_GetPtrXY proc _X, _Y
mov eax, _Y
mul [ecx].C2DArray.dwWidth;_X
add eax,_X
icall ecx, C2DArray, GetPtrToNth, eax
.if eax==-1 ;Failure?
xor eax,eax ;Our failure returncode
.endif
ret
C2DArray_GetPtrXY endp

C2DArray_SetXY proc _X,_Y,pElement
icall ecx, C2DArray, GetPtrXY, _X, _Y
.if eax!=0
xor ebx,ebx
mov esi,eax
mov edi,pElement
.while ebx<[ecx].C2DArray.dwElementSize
mov al,byte ptr[esi]
mov byte ptr[edi],al
inc esi
inc edi
inc ebx
.endw
return TRUE
.endif
return FALSE
C2DArray_SetXY endp




;Author - Homer
;Date - 13 July 2004
;Title - CArrayManager class
;Description -
;// This class manages an array of entries of arbitrary size.
;// By default it acts like CVector, with DWORD sized elements.
;// Use the SetElementSize method to set the elementsize to anything you choose.
;// Added a Count field which is updated internally and reflects the #entries in the array

class CArrayManager, , C++ compatible
void SetElementSize:dwSize ;Tell the instance of CArrayManager what size its elements are
void GetPtrToNth:dwIndex ;Returns pointer to Nth element, or -1 = out of bounds
void push_back:pData ;This method will store an entire struct to the array as a single element.
void pop_back:pReturn ;This method will return an entire data element to your receiving struct
void Find ;<-- Find element in array, return -1 or address of element in array
void FindIndex ;<-- Find element in array, return -1 or INDEX of element in array
void Delete:dwIndex ;<-- Delete array element by index, array content is shifted back to fill gap
long pBase ;<-- This points to the BaseAddress of the array memory
long dwNextPlace ;<-- This holds offset to next empty write location
long cbSize ;<-- This holds the current size of the buffer in bytes
long dwElementSize
long Count
endclass

CArrayManager_GetPtrToNth proc dwIndex
mov eax,[ecx].CArrayManager.dwElementSize
mov ebx,[ecx].CArrayManager.cbSize
sub ebx,eax
mul dwIndex
.if eax>=ebx ;BoundsChecking - are we trying to read beyond occupied arraymem?
return -1
.endif
add eax,[ecx].CArrayManager.pBase
ret
CArrayManager_GetPtrToNth endp

CArrayManager_SetElementSize proc dwSize
m2m [ecx].CArrayManager.dwElementSize, dwSize
ret
CArrayManager_SetElementSize endp

CArrayManager_CArrayManager proc ;*** CONSTRUCTOR ***
mov eax,1024 ;Initialize the array @ 1024 bytes (256 dword entries)
mov [ecx].CArrayManager.cbSize,eax
mov [ecx].CArrayManager.pBase, malloc (eax)
mov [ecx].CArrayManager.dwElementSize,4 ;<--dword sized by default
ret
CArrayManager_CArrayManager endp

CArrayManager_$CArrayManager proc ;*** DESTRUCTOR ***
free [ecx].CArrayManager.pBase ;Release the array memory
ret
CArrayManager_$CArrayManager endp

CArrayManager_push_back proc pData:DWORD
mov ebx,[ecx].CArrayManager.pBase ;Calculate the address of the next empty place
mov eax,[ecx].CArrayManager.dwNextPlace ;in the array (=pBase+dwNextPlace)
add ebx,eax
pushad
invoke RtlMoveMemory,ebx,pData,[ecx].CArrayManager.dwElementSize ;Store the data at the next place in array
popad
add eax,[ecx].CArrayManager.dwElementSize
;mov [ecx].CArrayManager.dwNextPlace,eax ;Increment the NextPlace index
.if eax>=[ecx].CArrayManager.cbSize ;If we are out of bounds
add eax,[ecx].CArrayManager.dwElementSize ;Grow the array
mov [ecx].CArrayManager.cbSize,eax ;noting the new size
mov ebx,[ecx].CArrayManager.pBase
mov [ecx].CArrayManager.pBase, remalloc ([ecx].CArrayManager.pBase, eax)
.endif
mov eax,[ecx].CArrayManager.dwElementSize
add [ecx].CArrayManager.dwNextPlace,eax
inc [ecx].CArrayManager.Count
ret
CArrayManager_push_back endp

CArrayManager_pop_back proc pReturn:DWORD
mov eax,[ecx].CArrayManager.dwElementSize
sub [ecx].CArrayManager.dwNextPlace,eax ;Decrement the NextPlace index
mov ebx,[ecx].CArrayManager.pBase ;Calculate the address of the last element
add ebx,[ecx].CArrayManager.dwNextPlace
pushad
invoke RtlMoveMemory,pReturn, ebx, [ecx].CArrayManager.dwElementSize
popad
invoke RtlZeroMemory,ebx,[ecx].CArrayManager.dwElementSize
dec [ecx].CArrayManager.Count
ret
CArrayManager_pop_back endp

CArrayManager_Delete proc uses esi edi dwIndex:DWORD
mov edi,[ecx].CArrayManager.pBase
mov eax,dwIndex
mul [ecx].CArrayManager.dwElementSize
add edi,eax
add eax,[ecx].CArrayManager.dwElementSize
.while eax<[ecx].CArrayManager.cbSize
mov ebx,[ecx].CArrayManager.dwElementSize
add ebx,edi
pushad
invoke RtlMoveMemory,edi,ebx,[ecx].CArrayManager.dwElementSize
popad
add eax,[ecx].CArrayManager.dwElementSize
add edi,[ecx].CArrayManager.dwElementSize
.endw
mov eax,[ecx].CArrayManager.dwElementSize
sub [ecx].CArrayManager.dwNextPlace,eax
dec [ecx].CArrayManager.Count
ret
CArrayManager_Delete endp

CArrayManager_Find proc pFind:DWORD
mov esi,pFind
mov ebx,[ecx].CArrayManager.pBase
.while ebx < [ecx].CArrayManager.dwNextPlace
push ebx
mov al,byte ptr[ebx]
.if byte ptr[esi]==al
mov ecx,[ecx].CArrayManager.dwElementSize
.while al==byte ptr[ebx] && ecx!=0
mov al,byte ptr[ebx]
inc esi
inc ebx
dec ecx
.endw
.if ecx==0
pop ebx
return ebx
.endif
.endif
pop ebx
add ebx,[ecx].CArrayManager.dwElementSize
add esi,[ecx].CArrayManager.dwElementSize
.endw
return -1
CArrayManager_Find endp

CArrayManager_FindIndex proc pFind:DWORD
local dwIndex
mov dwIndex,0
mov esi,pFind
mov ebx,[ecx].CArrayManager.pBase
.while ebx < [ecx].CArrayManager.dwNextPlace
push ebx
mov al,byte ptr[ebx]
.if byte ptr[esi]==al
mov ecx,[ecx].CArrayManager.dwElementSize
.while al==byte ptr[ebx] && ecx!=0
mov al,byte ptr[ebx]
inc esi
inc ebx
dec ecx
.endw
.if ecx==0
pop ebx
return dwIndex
.endif
.endif
pop ebx
add ebx,[ecx].CArrayManager.dwElementSize
add esi,[ecx].CArrayManager.dwElementSize
inc dwIndex
.endw
return -1
CArrayManager_FindIndex endp


Example snippet


set pArray as C2DArray
mov pArray , new (C2DArray)
pcall pArray.SetArraySize, 150, 100, 32 ;X,Y,ElementSize
pcall pArray.SetXY,101,0, pElement ;simple enough?
.
.
.
delete pArray
Posted on 2004-10-11 02:17:24 by Homer
Cool :) . Just make Find and FindIndex preserve esi and edi. Not suited for multithreaded apps, but is perfect for single-threaded.
Posted on 2004-10-12 14:10:39 by Ultrano
Yeah, I know it should have thread synchronisation of some kind (I used CriticalSection lock for CVector), and as soon as a project warrants it, I'll add it to appropriate baseclasses.. for now it seems ok - I just implemented a MazeGenerator using this code, worked great... speaking of which, that may be of some interest to you :)
Posted on 2004-10-13 01:23:22 by Homer
Cool :) but I make games only for PalmOS ^^"
Soon will be released this one:
, later - more and more games :grin:
I implemented macros in the GAS for ARM, but still no ATC :) My boss demands that I write most of the code in C ^^"
Posted on 2004-10-13 07:07:21 by Ultrano
Looks like fun :)
Posted on 2004-10-13 09:15:08 by Homer