File: dllmain.cpp
Size: 22131
Date: Tue, 08 May 2012 23:13:40 +0200
Type: cpp
#include "pbvm.h"

vm_state *last_vm = NULL;

value * get_lvalue(vm_state *vm, lvalue_ref *value_ref){
	lvalue *v=value_ref->ptr;
	//if (value_ref->isnull&1) return 0;

	switch(v->flag){
		case 0:// immediate value (local variable?)
			return v->value;
		case 1:// instance field?
			return ot_get_field_lv(vm, v->value, v->parent);
		default:// array element?
			return ot_get_field_item_lv(vm, v->value, v->parent, v->item);
	}
}

void Throw_Exception(vm_state *vm, wchar_t *text, ...){
	pb_class *exception_obj;
	va_list va;
	va_start(va,text);
	rt_create_obinst(vm,L"n_ex",&exception_obj);
	wchar_t x[512];
	wvsprintf(x,text,va);
	ob_set_ptr_field(vm,exception_obj,1,ob_dup_string(vm,x));
	GET_THROW(vm)=exception_obj;
}

void BuildKMPTable(wchar_t *find, int *kmp_table){
	int pos=2, cnd=0;
	
	kmp_table[0] = -1;
	if (!find[0]) return;

	kmp_table[1] = 0;
	if (!find[1]) return;

	while (find[pos]){
		if (find[pos -1] == find[cnd]){
			kmp_table[pos++] = ++cnd;
		}else if(cnd>0){
			if (find[cnd] == find[kmp_table[cnd]])
				cnd = kmp_table[cnd];
			if(cnd>0)
				cnd = kmp_table[cnd];
		}else{
			kmp_table[pos++] = 0;
		}
	}
}

wchar_t * StringSearch(wchar_t *start, wchar_t *find, int *kmp_table){
	wchar_t *p=start;
	long i=0;

	while(*p){
		if (*p == find[i]){
			i++;p++;
			if (!find[i]) return p - i;
		}else if (i>0){
			i=kmp_table[i];
		}else
			p++;
	}
	return NULL;
}

wchar_t * StringSearchi(wchar_t *start, wchar_t *find, int *kmp_table){
	wchar_t *p=start;
	long i=0;

	while(*p){
		if ((iswupper(*p)?towlower(*p):*p)==find[i]){
			i++;p++;
			if (!find[i]) return p - i;
		}else if (i>0){
			i=kmp_table[i];
		}else
			p++;
	}
	return NULL;
}

// boolean split(readonly source, readonly delim, ref string values[])
DWORD __declspec(dllexport) __stdcall Split (vm_state *vm, DWORD arg_count){
	wchar_t *source, *delim;
	DWORD source_null, delim_null, isnull;
	lvalue_ref *lv_values;
	value ret;

	last_vm = vm;

	source = (wchar_t *)ot_get_valptr_arg(vm, &source_null);
	delim = (wchar_t *)ot_get_valptr_arg(vm, &delim_null);

	lv_values=ot_get_next_lvalue_arg(vm, &isnull);

	ret.value=FALSE;
	ret.type=7;
	ret.flags=0x0500;
	
	if (source&&*source&&delim&&*delim){
		wchar_t *next, *last=source, *dest;
		DWORD delim_len, index=0, new_size;
		pb_array *values;
		int *kmp_table;

		delim_len=wcslen(delim);

		values = ot_array_create_unbounded(vm, MAKELONG(-1,6), 0);

		kmp_table=new int[delim_len+1];
		BuildKMPTable(delim, kmp_table);

		while(1){
			next=StringSearch(last, delim, kmp_table);
			//next=wcsstr(last, delim);
			if (next==NULL){
				new_size=wcslen(last);
			}else{
				new_size=next - last;
			}
			value *v=ot_array_index(vm, values, index);
			if (!(v->flags&IS_NULL))
				ot_free_val_ptr(vm, v);

			dest = (wchar_t *)pbstg_alc(vm, (new_size+1) *2, GET_HEAP(vm));
			wcsncpy(dest, last, new_size);
			dest[new_size]=0;
			
			v->value=(DWORD)dest;
			v->flags=0x0d00;
			v->type=6;
			if (!next) break;

			index++;
			last=next+delim_len;
		}
		delete kmp_table;

		ot_assign_ref_array(vm, lv_values->ptr, values, 0, 0);
		ret.value=TRUE;
	}
	ot_set_return_val(vm, &ret);
	return 1;
}

// boolean token(ref source, readonly delim, ref string segment)
// calling this repeatedly is quite slow, you should try to use the split method instead, but this method is sometimes useful.
DWORD __declspec(dllexport) __stdcall Token2 (vm_state *vm, DWORD arg_count){
	wchar_t *source, *delim, *next, *dest;
	DWORD isnull, delim_len, new_size;
	lvalue_ref *lv_source, *lv_token;
	value ret, *v_source;
	
	last_vm = vm;

	ret.value=TRUE;
	ret.type=7;
	ret.flags=0x0500;
	
	lv_source=ot_get_next_lvalue_arg(vm, &isnull);
	v_source = get_lvalue(vm, lv_source);
	if (!v_source || !v_source->value || !*((wchar_t *)v_source->value)) ret.value=FALSE;

	delim = (wchar_t *)ot_get_valptr_arg(vm, &isnull);
	if (isnull || !delim || !*delim ) ret.value=FALSE;

	lv_token=ot_get_next_lvalue_arg(vm, &isnull);
	
	if (ret.value){
		source = (wchar_t *)v_source->value;

		next=wcsstr(source, delim);

		if (next==NULL){
			new_size=wcslen(source);
			
			dest = (wchar_t *)pbstg_alc(vm, (new_size+1) *2, GET_HEAP(vm));
			wcsncpy(dest, source, new_size);
			dest[new_size]=0;

			ot_assign_ref_string(vm, lv_token->ptr, dest, 0);
			ot_assign_ref_string(vm, lv_source->ptr, NULL, 1);
		}else{
			new_size = (next - source);

			dest = (wchar_t *)pbstg_alc(vm, (new_size+1) *2, GET_HEAP(vm));
			wcsncpy(dest, source, new_size);
			dest[new_size]=0;

			ot_assign_ref_string(vm, lv_token->ptr, dest, 0);

			delim_len=wcslen(delim);
			next+=delim_len;
			new_size = wcslen(next);

			dest = (wchar_t *)pbstg_alc(vm, (new_size+1) *2, GET_HEAP(vm));
			wcsncpy(dest, next, new_size);
			dest[new_size]=0;

			ot_assign_ref_string(vm, lv_source->ptr, dest, 0);
		}
	}
	if (!ret.value){
		ot_assign_ref_string(vm, lv_token->ptr, NULL, 1);
	}
	ot_set_return_val(vm, &ret);
	return 1;}

// boolean token(readonly source, readonly delim, ref long pos, ref string segment)
// calling this repeatedly is quite slow, you should try to use the split method instead
DWORD __declspec(dllexport) __stdcall Token (vm_state *vm, DWORD arg_count){
	wchar_t *source, *delim, *next, *dest;
	DWORD isnull, len, end, pos, delim_len, new_size;
	lvalue_ref *lv_pos, *lv_token;
	value ret, *v_pos;
	
	last_vm = vm;

	ret.value=TRUE;
	ret.type=7;
	ret.flags=0x0500;
	
	source = (wchar_t *)ot_get_valptr_arg(vm, &isnull);
	if (isnull || !source || !*source ) ret.value=FALSE;
	delim = (wchar_t *)ot_get_valptr_arg(vm, &isnull);
	if (isnull || !delim || !*delim ) ret.value=FALSE;

	lv_pos=ot_get_next_lvalue_arg(vm, &isnull);
	v_pos = get_lvalue(vm, lv_pos);
	if (!v_pos) ret.value=FALSE;
	
	lv_token=ot_get_next_lvalue_arg(vm, &isnull);
	
	if (ret.value){
		// comments show worked example for; token(" X ","X",pos=1,ret)
		len=wcslen(source);
		// len=3
		pos=v_pos->value;
		if (pos<1)
			pos=1;

		if (pos>len){
			ret.value=FALSE;
		}else{
			delim_len=wcslen(delim);
			// delim_len=1

			next=wcsstr(source + pos -1, delim);
			// next -> " ^X "

			if (next==NULL){
				end=len+1;
			}else{
				end = (next - source);
				// end = 1
			}
			
			ot_assign_ref_long(vm, lv_pos->ptr, end + delim_len + 1, 0);
			// pos = 3

			new_size = end - pos + 1;
			// new_size = 1

			dest = (wchar_t *)pbstg_alc(vm, (new_size+1) *2, GET_HEAP(vm));
			wcsncpy(dest, source + pos -1, new_size);
			dest[new_size]=0;

			ot_assign_ref_string(vm, lv_token->ptr, dest, 0);
		}
	}
	if (!ret.value){
		ot_assign_ref_long(vm, lv_pos->ptr, 1, 0);
		ot_assign_ref_string(vm, lv_token->ptr, NULL, 1);
	}
	ot_set_return_val(vm, &ret);
	return 1;
}

// boolean next_tag(readonly string as_source, readonly string as_start, readonly string as_end, ref long al_start_pos, ref string as_tag)
DWORD __declspec(dllexport) __stdcall Next_Tag (vm_state *vm, DWORD arg_count){
	wchar_t *source, *start_delim, *end_delim;
	DWORD isnull;
	lvalue_ref *lv_pos, *lv_tag;
	value ret, *v_pos;
	
	last_vm = vm;

	ret.value=TRUE;
	ret.type=7;
	ret.flags=0x0500;
	
	source = (wchar_t *)ot_get_valptr_arg(vm, &isnull);
	if (isnull || source==NULL || *source==0) ret.value=FALSE;
	start_delim = (wchar_t *)ot_get_valptr_arg(vm, &isnull);
	if (isnull || start_delim==NULL || *start_delim==0) ret.value=FALSE;
	end_delim = (wchar_t *)ot_get_valptr_arg(vm, &isnull);
	if (isnull || end_delim==NULL || *end_delim==0) ret.value=FALSE;
	
	lv_pos=ot_get_next_lvalue_arg(vm, &isnull);
	v_pos = get_lvalue(vm, lv_pos);
	if (!v_pos) ret.value=FALSE;

	lv_tag=ot_get_next_lvalue_arg(vm, &isnull);
	
	if (ret.value){
		DWORD len, pos;

		// comments show worked example for; token(" <X> ","<",">",pos=1,ret)
		len=wcslen(source);
		// len=5
		pos=v_pos->value;
		if (pos<1)
			pos=1;

		if (pos>len){
			ret.value=FALSE;
		}else{
			wchar_t *start, *test, *end, *dest;
			DWORD start_len, end_len, new_size;

			start_len=wcslen(start_delim);
			// start_len=1
			end_len=wcslen(end_delim);
			// end_len=1

			test=source + pos -1;

			while (1){
				// find the next end delimiter
				end=wcsstr(test, end_delim);
				// end -> " <X^> "
				if (end==NULL){
					ret.value=FALSE;
					break;
				}

				// find the last start delimiter before the end delimiter
				start=NULL;
				test=source;
				while(1){
					test = wcsstr(test, start_delim);
					if (test==NULL || test > end) break;
					start=test++;
				}
				
				if (start==NULL){
					// try again, and find the next end delimiter
					test=end + 1;
					continue;
				}
				
				// start -> " ^<X> "
				new_size = end - start - start_len;
				// new_size = 1

				dest = (wchar_t *)pbstg_alc(vm, (new_size+1) *2, GET_HEAP(vm));
				wcsncpy(dest, start + start_len, new_size);
				dest[new_size]=0;
				ot_assign_ref_string(vm, lv_tag->ptr, dest, 0);
				// as_tag="X"

				ot_assign_ref_long(vm, lv_pos->ptr, start - source +1, 0);
				// al_start_pos=2

				break;
			}
		}
	}

	if (!ret.value){
		ot_assign_ref_long(vm, lv_pos->ptr, 1, 0);
		ot_assign_ref_string(vm, lv_tag->ptr, NULL, 1);
	}

	ot_set_return_val(vm, &ret);
	return 1;
}

int __cdecl compare( void *context, const void *arg1, const void *arg2 )
{
   /* Compare all of both strings: */
   return wcscmp( (( wchar_t** )context)[*(long *)arg1], (( wchar_t** )context)[*(long *)arg2]);
}

int __cdecl comparei( void *context, const void *arg1, const void *arg2 )
{
   /* Compare all of both strings: */
   return _wcsicmp( (( wchar_t** )context)[*(long *)arg1], (( wchar_t** )context)[*(long *)arg2]);
}

DWORD __declspec(dllexport) __stdcall Sort (vm_state *vm, DWORD arg_count){
	DWORD ignored;
	value ret;
	long count;
	DWORD insensitive=FALSE;
	lvalue_ref *lv_array1, *lv_array2;
	value *v_array1, *v_array2;
	pb_array *array1, *array2;

	last_vm = vm;

	ret.type=1;
	ret.flags=0x500;
	ret.value=1;

	lv_array1 = ot_get_next_lvalue_arg(vm,&ignored);
	v_array1 = get_lvalue(vm, lv_array1);
	if (v_array1){
		array1=(pb_array *)v_array1->value;
		count = ot_array_num_items(vm, array1);
	}else{
		ret.value=-1;
	}

	if (arg_count>=2){
		insensitive = ot_get_simple_intarg(vm, &ignored);
	}

	if (arg_count>=3){
		lv_array2 = ot_get_next_lvalue_arg(vm, &ignored);
		v_array2 = get_lvalue(vm, lv_array2);
		if (v_array2){
			array2=(pb_array *)v_array2->value;
			if (count != ot_array_num_items(vm, array2)){
				ret.value=-1;
			}
		}else{
			ret.value=-1;
		}
	}

	if (ret.value==1){
		
		wchar_t **strings;
		value *values;
		long *index;

		strings=new wchar_t*[count];
		index = new long[count];

		for (long i=0;i<count;i++){
			value *v=ot_array_index(vm, array1, i);
			strings[i]=(wchar_t*)v->value;
			index[i]=i;
		}

		if (arg_count>=3){
			values=new value[count];
			for (long i=0;i<count;i++){
				value *v=ot_array_index(vm, array2, i);
				values[i].value=v->value;
				values[i].flags=v->flags;
				values[i].type=v->type;
			}
		}
		
		qsort_s( index, count, sizeof( long ), (insensitive?comparei:compare), strings );

		for (long i=0;i<count;i++){
			value *v = ot_array_index(vm, array1, i);
			v->value=(DWORD)strings[index[i]];
		}
		if (arg_count>=3){
			for (long i=0;i<count;i++){
				value *v = ot_array_index(vm, array2, i);
				v->value=values[index[i]].value;
				v->flags=values[index[i]].flags;
				v->type=values[index[i]].type;
			}
			delete values;
		}
		delete strings;
		delete index;
	}

	ot_set_return_val(vm, &ret);
	return 1;
}

/*usage:
	long ll_values[]={1,2,3,4,5}
	ll_i = index(4, ll_values)
*/

DWORD __declspec(dllexport) __stdcall Index (vm_state *vm, DWORD arg_count){
	value ret;
	last_vm = vm;

	value *v_find = ot_get_next_evaled_arg_no_convert(vm);
	value *v_array = ot_get_next_evaled_arg_no_convert(vm);
	
	int len=0;
	void *p;
	bool ptr = false;

	switch(v_find->type){
		case pbvalue_int:
		case pbvalue_boolean:
		case pbvalue_uint:
		case pbvalue_char:
			len=2;
			break;
		case pbvalue_byte:
			len=1;
			break;
		case pbvalue_long:
		case pbvalue_ulong:
		case pbvalue_real:
			len=4;
			break;
		case pbvalue_longlong:
		case pbvalue_double:
			len=8;
			ptr=true;
			break;
		case pbvalue_dec:
			len=16;
			ptr=true;
			break;
		case pbvalue_blob:
			ptr=true;
			{
				blob *b=(blob*)v_find->value;
				// since the length is at the start, if it doesn't match memcmp won't look any further into potentially unallocated memory
				len = b->len+4; 
			}
			break;
		case pbvalue_string:
			ptr=true;
			break;
		case pbvalue_date:
		case pbvalue_time:
		case pbvalue_datetime:
			len=12;
			ptr=true;
			break;
	}

	if (ptr){
		p=(char*)v_find->value;
	}else{
		p=(char*)&v_find->value;
	}
	ret.value=0;
	ret.type=pbvalue_long;
	ret.flags=0x100;
	
	pb_array *parray=(pb_array *)v_array->value;

	long count = ot_array_num_items(vm, parray);
	for (long i=0;i<count;i++){
		value *v=ot_array_index(vm, parray, i);
		if (v->type == v_find->type){
			if (v_find->flags&IS_NULL){
				if (v->flags&IS_NULL){
					ret.value=i+1;
					break;
				}
			}else if (v->flags&IS_NULL){
				continue;
			}else if (v->type == pbvalue_string){
				if (wcscmp((wchar_t*)p,(wchar_t*)v->value)==0){
					ret.value=i+1;
					break;
				}
			}else if (ptr){
				if (memcmp(p,(void*)v->value,len)==0){
					ret.value=i+1;
					break;
				}
			}else{
				if (memcmp(p,(void*)&v->value,len)==0){
					ret.value=i+1;
					break;
				}
			}
		}
	}
	
	ot_set_return_val(vm, &ret);
	return 1;
}

// in most cases should perform about the same as pos
// doesn't have some of the same worst cases as pos though...
DWORD __declspec(dllexport) __stdcall Fast_Pos (vm_state *vm, DWORD arg_count){
	DWORD source_null, find_null;
	wchar_t *source, *find;
	value v;

	last_vm = vm;

	source = (wchar_t *)ot_get_valptr_arg(vm, &source_null);
	find = (wchar_t *)ot_get_valptr_arg(vm, &find_null);
	
	v.value=0;
	v.type=pbvalue_long;
	v.flags=0x100;
	if (!(source_null||find_null)){
		int find_len = wcslen(find);
		if (find_len>0){
			int *kmp_table = new int[find_len];
			BuildKMPTable(find, kmp_table);
			wchar_t *p=StringSearch(source, find, kmp_table);
			delete kmp_table;

			if (p)
				v.value=p - source +1;
		}
	}
	ot_set_return_val(vm, &v);
	return 1;
}

// should perform better than last pos in all cases.
// in some synthetic examples it should be MUCH faster.
DWORD __declspec(dllexport) __stdcall Last_Pos (vm_state *vm, DWORD arg_count){
	DWORD source_null, find_null;
	wchar_t *source, *find;
	value v;

	last_vm = vm;

	source = (wchar_t *)ot_get_valptr_arg(vm, &source_null);
	find = (wchar_t *)ot_get_valptr_arg(vm, &find_null);
	
	v.value=0;
	v.type=pbvalue_long;
	v.flags=0x100;
	
	if (!(source_null||find_null)){
		int find_len = wcslen(find);
		
		if (find_len>0){
			int *kmp_table = new int[find_len];
			find_len --;
			int find_pos = find_len;
			int cnd=find_len;
			int pos=cnd;

			kmp_table[pos--]=cnd;
			if (pos>=0){
				kmp_table[pos--]=cnd;
				while (pos>=0){
					if (find[pos+1]==find[cnd]){
						kmp_table[pos--] = --cnd;
					}else if (cnd < find_len){
						if (find[cnd]==find[kmp_table[cnd]])
							cnd=kmp_table[cnd];
						if (cnd < find_len)
							cnd=kmp_table[cnd];
					}else{
						kmp_table[pos--] = find_len;
					}
				}
			}
			wchar_t *p=source+wcslen(source) -1;
			while (p>=source){
				if (*p==find[find_pos]){
					if (find_pos==0){
						v.value=p - source + 1;
						break;
					}
					find_pos --;
					p --;
				}else if (find_pos<find_len){
					find_pos= kmp_table[find_pos];
				}else{
					p --;
				}
			}
			delete kmp_table;
		}
	}
	ot_set_return_val(vm, &v);
	return 1;
}

// string Replace_All (readonly string source, readonly string find, readonly string replace [, boolean case_sensitive])
DWORD __declspec(dllexport) __stdcall Replace_All (vm_state *vm, DWORD arg_count){
	DWORD source_null, find_null, replace_null, ignored;
	wchar_t *source, *find, *replace;
	DWORD insensitive=FALSE;
	value v;

	last_vm = vm;

	source = (wchar_t *)ot_get_valptr_arg(vm, &source_null);
	find = (wchar_t *)ot_get_valptr_arg(vm, &find_null);
	replace = (wchar_t *)ot_get_valptr_arg(vm, &replace_null);
	
	if (arg_count>=4)
		insensitive = ot_get_simple_intarg(vm, &ignored);

	v.value=0;
	v.flags=0x0d01;
	v.type=pbvalue_string;

	if (!(source_null||find_null)){
		long count=0, buff_size, len_find, len_replace=0;
		int *kmp_table;
		wchar_t *p, *dest, *last, *find_copy;

		len_find=wcslen(find);
		
		if (len_find>0){
			kmp_table=new int[len_find+1];
			if (insensitive){
				find_copy=new wchar_t[len_find+1];
				// force the find string to lower case... er...
				for (int i=0;i<len_find+1;i++){
					find_copy[i]=(iswupper(find[i])?towlower(find[i]):find[i]);
				}

				BuildKMPTable(find_copy, kmp_table);
				p=source;
				while ((p=StringSearchi(p, find_copy, kmp_table))!=NULL){
					count++;
					p+=len_find;
				}
			}else{
				BuildKMPTable(find, kmp_table);
				p=source;
				while ((p=StringSearch(p, find, kmp_table))!=NULL){
					count++;
					p+=len_find;
				}
			}
		}

		if (count>0){
			if (replace_null){
				len_replace=0;
			}else{
				len_replace=wcslen(replace);
			}
			buff_size=wcslen(source) + (count * len_replace) - (count * len_find) +1;
		}else{
			buff_size=wcslen(source)+1;
		}

		dest = (wchar_t*)pbstg_alc(vm, buff_size *2, GET_HEAP(vm));
		*dest=0;
		v.value=(DWORD)dest;
		v.flags=0x0d00;

		if (count>0){
			p=source;
			last=p;
			if (insensitive){
				while ((p=StringSearchi(p, find_copy, kmp_table))!=NULL){
					long cpy = (p - last);
					wcsncpy(dest, last, cpy);
					dest+=cpy;
					*dest=0;
					if (len_replace>0){
						wcscpy(dest,replace);
						dest+=len_replace;
					}
					p+=len_find;
					last=p;
				}
				delete find_copy;
			}else{
				while ((p=StringSearch(p, find, kmp_table))!=NULL){
					long cpy = (p - last);
					wcsncpy(dest, last, cpy);
					dest+=cpy;
					*dest=0;
					if (len_replace>0){
						wcscpy(dest,replace);
						dest+=len_replace;
					}
					p+=len_find;
					last=p;
				}
			}
			delete kmp_table;
			wcscpy(dest,last);
		}else{
			wcscpy(dest,source);
		}
	}
	ot_set_return_val(vm, &v);
	return 1;
}

// boolean append(ref string, readonly string)
// boolean append(ref blob, readonly string)

DWORD __declspec(dllexport) __stdcall Append (vm_state *vm, DWORD arg_count){
	DWORD isnull;
	value v;
	
	last_vm = vm;

	v.value=TRUE;
	v.type=7;
	v.flags=0x0500;

	lvalue_ref *lv_source=ot_get_next_lvalue_arg(vm, &isnull);
	value *v_source = get_lvalue(vm, lv_source);
	if (v_source->flags&IS_NULL) v.value=FALSE;

	if (v.value){
		int source_len=0;
		wchar_t *dest_ptr=NULL;
		int realloc_size;

		int buff_size = pbstg_sz(vm,(void *)v_source->value);
		const int count = arg_count -1;
		wchar_t **strings = (wchar_t**)_alloca(sizeof(wchar_t**) * count);
		int *string_lengths = (int *)_alloca(sizeof(int) * count);
		int append_len=0;

		for (int i=0;i<count;i++){
			strings[i] = (wchar_t *)ot_get_valptr_arg(vm, &isnull);
			if (isnull || !*strings[i]){
				string_lengths[i]=0;
				continue;
			}
			string_lengths[i] = wcslen(strings[i]);
			append_len+=string_lengths[i]*2;
		}

		if (v_source->type==pbvalue_string){
			dest_ptr = (wchar_t *)v_source->value;
			
			if (*dest_ptr){
				if (buff_size>=32){
					if (*(unsigned short*)(v_source->value + buff_size - 6)==0xABCD){
						source_len = *(int*)(v_source->value + buff_size - 4);
						if (*(unsigned short*)(v_source->value + source_len+2)!=0xABCD)
							source_len=0;
					}
				}

				if (source_len==0){
					source_len = wcslen(dest_ptr)*2;
				}
			}
			realloc_size = source_len + append_len + 10;
		}else{
			source_len = ((blob *)v_source->value)->len+2;
			if (source_len<4 || *(wchar_t*)(v_source->value + source_len))
				source_len+=2;
			realloc_size = source_len + append_len + 2;
		}
		
		if (realloc_size>6144){
			// round up to the nearest 4k
			realloc_size|=0xFFF;
		}
		
		if (realloc_size > buff_size){
			v_source->value = (DWORD)pbstg_realc(vm, (void *)v_source->value, realloc_size, GET_HEAP(vm));
			buff_size = pbstg_sz(vm,(void *)v_source->value);
		}

		dest_ptr = (wchar_t *)(v_source->value + source_len);
		for (int i=0;i<count;i++){
			if (string_lengths[i]==0)
				continue;

			if (strings[i] == dest_ptr){
				// if you're trying to append the source string onto the source string, it may have just moved.
				// (that's also why wcsncpy is used below, or we'd keep appending the string till we hit an invalid page of memory)
				strings[i] = (wchar_t *)v_source->value;
			}
			wcsncpy(dest_ptr, strings[i], string_lengths[i]);
			dest_ptr+=string_lengths[i];
		}

		*dest_ptr=0;

		if (v_source->type==pbvalue_string){
			if (buff_size>=32){
				*(unsigned short*)(v_source->value+source_len+append_len+2)=0xABCD;
				*(unsigned short*)(v_source->value+buff_size-6)=0xABCD;
				*(int*)(v_source->value+buff_size-4)=source_len + append_len;
			}
		}else{
			((blob *)v_source->value)->len = source_len+append_len -2;
		}
	}

	ot_set_return_val(vm, &v);
	return 1;
}


// string value_info(readonly any)
DWORD __declspec(dllexport) __stdcall Value_Info (vm_state *vm, DWORD arg_count){
//	DWORD isnull;
	value v;
	wchar_t info[256];

	last_vm = vm;

	value *v_value = ot_get_next_evaled_arg_no_convert(vm);
	
	DWORD len=0;
	DWORD size=0;
	
	if (!(v_value->flags&(IS_NULL|IS_ARRAY))){
		if (v_value->type==pbvalue_string){
			len = (wcslen((wchar_t*)v_value->value)+1)*2;
		}else if (v_value->type==pbvalue_blob){
			len = ((blob*)v_value->value)->len + 4;
		}
		if (len>0)
			size = pbstg_sz(vm,(void *)v_value->value);
	}

	wnsprintf(info, 256, L"Type: %d, Flags: %u, Value: %d, UsedLen: %d, BuffSize %d",v_value->type,v_value->flags, v_value->value, len, size);

	len=wcslen(info);
	wchar_t *dest = (wchar_t *)pbstg_alc(vm, (len+1)*2, GET_HEAP(vm));
	wcscpy(dest,info);
	v.value=(DWORD)dest;
	v.flags=0x0d00;
	v.type=pbvalue_string;

	ot_set_return_val(vm, &v);
	return 1;
}


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		Install_Crash_Hook();
		break;
	case DLL_THREAD_ATTACH:
		break;
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		Uninstall_Crash_Hook();
		break;
	}
	return TRUE;
}