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;
}