static GWidget* itemview_make( UThread* ut, UBlockIter* bi, const GWidgetClass* wclass ) { GItemView* ep; UIndex itemBlkN; const UCell* arg[3]; //int ok; if( ! gui_parseArgs( ut, bi, wclass, itemview_args, arg ) ) return 0; ep = (GItemView*) gui_allocWidget( sizeof(GItemView), wclass ); assert( sizeof(GLint) == sizeof(GLsizei) ); ur_arrInit( ep->fc + 0, sizeof(GLint), 0 ); ur_arrInit( ep->fc + 1, sizeof(GLint), 0 ); glGenBuffers( 2, ep->vbo ); ep->vboSize[0] = ep->vboSize[1] = 0; ep->use_color = -1; ep->selCol = -1; ep->selRow = -1; ep->itemCount = 0; ep->itemWidth = 0; ep->itemHeight = 0; ep->scrollY = 0; ep->modVbo = 0; ep->updateMethod = IV_UPDATE_2; ep->layoutBlkN = UR_INVALID_BUF; ep->selectBgBlkN = UR_INVALID_BUF; ep->bindCtxN = UR_INVALID_BUF; ep->actionBlkN = UR_INVALID_BUF; itemBlkN = arg[0]->series.buf; if( ur_isShared( itemBlkN ) ) ep->dataBlkN = UR_INVALID_BUF; else ep->dataBlkN = itemBlkN; // Optional action block. if( arg[2] ) ep->actionBlkN = arg[2]->series.buf; //-------------------------------------------- // Parse layout block [size coord! item block! selected block!] { UBlockIterM bi; UBuffer* ctx; if( ur_blkSliceM( ut, &bi, arg[1] ) != UR_OK ) goto fail; if( (bi.end - bi.it) < 3 ) goto bad_layout; ctx = gui_styleContext( ut ); if( ctx ) ur_bind( ut, bi.buf, ctx, UR_BIND_THREAD ); if( ur_is(bi.it, UT_COORD) ) { // Fixed layout. static char itemStr[] = "item"; UBuffer* blk; if( ! ur_is(bi.it + 1, UT_BLOCK) || ! ur_is(bi.it + 2, UT_BLOCK) ) goto bad_layout; ep->itemWidth = bi.it->coord.n[0]; ep->itemHeight = bi.it->coord.n[1]; //printf( "KR item dim %d,%d\n", ep->itemWidth, ep->itemHeight ); ++bi.it; ep->layoutBlkN = bi.it->series.buf; ep->bindCtxN = ur_makeContext( ut, 1 ); // gc! ctx = ur_buffer( ep->bindCtxN ); ur_ctxAppendWord( ctx, ur_intern( ut, itemStr, 4 ) ); if( ! (blk = ur_bufferSerM( bi.it )) ) goto fail; ur_bind( ut, blk, ctx, UR_BIND_THREAD ); ++bi.it; ep->selectBgBlkN = bi.it->series.buf; } #ifdef ITEMVIEW_BOX_LAYOUT else { // Automatic layout. glEnv.guiParsingItemView = 1; ok = gui_makeWidgets( ut, arg[1], &ep->wid, 0 ); glEnv.guiParsingItemView = 0; if( ! ok ) goto fail; //ep->wid.child->flags |= GW_HIDDEN; } #endif } return (GWidget*) ep; bad_layout: ur_error( ut, UR_ERR_SCRIPT, "Invalid item-view layout" ); fail: itemview_free( (GWidget*) ep ); return 0; }
static void itemview_layout( GWidget* wp ) { UCell* rc; /* UCell* it; UBuffer* blk; int row, rowCount; int col, colCount; int itemY; */ UCell* style = glEnv.guiStyle; UThread* ut = glEnv.guiUT; //UIndex strN = 0; EX_PTR; /* DPCompiler* save; DPCompiler dpc; */ #ifdef ITEM_HEADER if( ep->headerBlkN <= 0 ) return; #endif if( ep->dataBlkN <= 0 ) return; itemview_calcItemHeight( ut, ep ); //itemY = wp->area.y + wp->area.h - ep->itemHeight; if( ep->use_color == -1 ) { rc = style + CI_STYLE_WIDGET_SH; if( ur_is(rc, UT_CONTEXT) ) { const Shader* shad = shaderContext( ut, rc, 0 ); if( shad ) { ep->use_color = glGetUniformLocation( shad->program, "use_color" ); //printf( "KR use_color %d\n", ep->use_color ); } } } #if 0 // Compile draw list for visible items. save = ur_beginDP( &dpc ); if( save ) dpc.shaderProg = save->shaderProg; // Header blk = ur_buffer( ep->headerBlkN ); it = blk->ptr.cell; colCount = ep->colCount = blk->used; for( col = 0; col < colCount; ++col, ++it ) { rc = style + CI_STYLE_LABEL; if( ur_is(it, UT_STRING) ) { *rc = *it; } rc = style + CI_STYLE_AREA; rc->coord.len = 4; rc->coord.n[0] = wp->area.x + (col * MIN_COLW); rc->coord.n[1] = itemY; rc->coord.n[2] = MIN_COLW; rc->coord.n[3] = ep->itemHeight; rc = style + CI_STYLE_LIST_HEADER; if( ur_is(rc, UT_BLOCK) ) ur_compileDP( ut, rc, 1 ); } itemY -= ep->itemHeight; // Items blk = ur_buffer( ep->dataBlkN ); it = blk->ptr.cell; rowCount = blk->used / colCount; for( row = 0; row < rowCount; ++row ) { for( col = 0; col < colCount; ++col, ++it ) { rc = style + CI_STYLE_LABEL; if( ur_is(it, UT_STRING) ) { *rc = *it; } else { UBuffer* str; if( ! strN ) strN = ur_makeString( ut, UR_ENC_LATIN1, 32 ); ur_initSeries( rc, UT_STRING, strN ); str = ur_buffer( strN ); str->used = 0; ur_toStr( ut, it, str, 0 ); } rc = style + CI_STYLE_AREA; rc->coord.len = 4; rc->coord.n[0] = wp->area.x + (col * MIN_COLW); rc->coord.n[1] = itemY; rc->coord.n[2] = MIN_COLW; rc->coord.n[3] = ep->itemHeight; rc = style + ((row == ep->selRow) ? CI_STYLE_LIST_ITEM_SELECTED : CI_STYLE_LIST_ITEM); if( ur_is(rc, UT_BLOCK) ) ur_compileDP( ut, rc, 1 ); } itemY -= ep->itemHeight; } ur_endDP( ut, ur_buffer(ep->dp[0]), save ); #endif }
/* Updates DrawContext attr & drawCount. Return UR_OK/UR_THROW. */ int itemview_parse( DrawContext* dc, UThread* ut, UBlockIter* bi, GWidget* wp ) { const UCell* arg[3]; UAtom atom; int opcode; float x, y; while( bi->it != bi->end ) { if( ur_is(bi->it, UT_WORD) ) { atom = ur_atom( bi->it ); opcode = ur_atomsSearch( glEnv.drawOpTable, DOP_COUNT, atom ); switch( opcode ) { case DOP_IMAGE: { QuadDim qd; int16_t rect[4]; const int16_t* coord; FETCH_ARGS( iv_args_image ); //printf( "KR IV image\n" ); coord = arg[0]->coord.n; rect[0] = coord[0] + dc->penX; rect[1] = coord[1] + dc->penY; rect[2] = coord[2]; rect[3] = coord[3]; quad_init( &qd, rect, arg[1]->coord.n, arg[2]->coord.n ); quad_emitVT( &qd, dc->attr, dc->attr + 3, AttrCount ); vbo_setVec3( dc->attr + 5, AttrCount, 6, dc->color ); dc->attr += 6 * AttrCount; dc->drawCount += 6; } break; case DOP_COLOR: FETCH_ARGS( iv_args_color ); dc->color[0] = (GLfloat) ur_int( arg[0] ); break; case DOP_FONT: FETCH_ARGS( iv_args_font ); dc->fontN = ur_fontTF( arg[0] ); break; case DOP_TEXT: FETCH_ARGS( iv_args_text ); x = dc->penX + (float) arg[0]->coord.n[0]; y = dc->penY + (float) arg[0]->coord.n[1]; { DrawTextState dts; UBinaryIter si; int glyphCount; //printf( "KR IV text\n" ); dp_toString( ut, arg[1], &si ); vbo_drawTextInit( &dts, (TexFont*) ur_buffer( dc->fontN )->ptr.v, x, y ); dts.emitTris = 1; glyphCount = vbo_drawText( &dts, dc->attr + 3, dc->attr, AttrCount, si.it, si.end ); glyphCount *= 6; vbo_setVec3( dc->attr + 5, AttrCount, glyphCount, dc->color ); /* while( si.it != si.end ) putchar( *si.it++ ); putchar( '\n' ); */ dc->attr += glyphCount * AttrCount; dc->drawCount += glyphCount; } break; default: goto invalid_op; } } else { goto invalid_op; } } return UR_OK; invalid_op: return ur_error( ut, UR_ERR_SCRIPT, "Invalid item-view instruction" ); }
static void itemview_rebuildAttr( UThread* ut, GItemView* ep, const UBuffer* items, int page ) { #define MAX_ITEM_TRIS 400 #define MAX_DEC_TRIS 100 #define TRI_ATTR_BYTES (3 * AttrCount * sizeof(GLfloat)) DrawContext dc; UBlockIter bi; UBlockIter layout; UBuffer* fcBuf; const UCell* loStart; UCell* cell; float* attr; int bsize; int height = ep->itemHeight; int caOffset = items->used + DECORATION_GROUPS; // Reserve triangles for each item. ur_binReserve( &glEnv.tmpBin, (MAX_DEC_TRIS + (items->used * MAX_ITEM_TRIS)) * TRI_ATTR_BYTES ); attr = glEnv.tmpBin.ptr.f; // Reserve first & count arrays. fcBuf->used tracks the number of items // and view decorations, which is half the number of integers used for // both arrays. fcBuf = &ep->fc[ page ]; ur_arrReserve( fcBuf, caOffset * 2 ); fcBuf->used = 0; dc.attr = attr; dc.drawFirst = 0; dc.penX = 0.0; #if 1 // Build selected background tris. dc.drawCount = 0; if( ep->selRow > -1 ) { dc.color[0] = 1.0; dc.color[1] = dc.color[2] = 0.0; dc.penY = ep->wid.area.h; cell = glEnv.guiStyle + CI_STYLE_AREA; //ur_setId(cell, UT_COORD) cell->coord.len = 4; cell->coord.n[0] = 0; cell->coord.n[1] = (ep->selRow + 1) * -height; cell->coord.n[2] = ep->itemWidth; cell->coord.n[3] = height; layout.buf = ur_buffer( ep->selectBgBlkN ); layout.it = layout.buf->ptr.cell; layout.end = layout.it + layout.buf->used; if( ! itemview_parse( &dc, ut, &layout, (GWidget*) ep ) ) { boron_reset(ut); printf( "KR itemview_parse failed\n" ); } } fcBuf->ptr.i[ fcBuf->used ] = dc.drawFirst; fcBuf->ptr.i[ fcBuf->used + caOffset ] = dc.drawCount; ++fcBuf->used; dc.attr = attr + MAX_DEC_TRIS * 3 * AttrCount; dc.drawFirst = MAX_DEC_TRIS * 3; #endif dc.penY = ep->wid.area.h; // Setup the data item and layout iterators. bi.it = items->ptr.cell; bi.end = bi.it + items->used; layout.buf = ur_buffer( ep->layoutBlkN ); loStart = layout.buf->ptr.cell; layout.end = loStart + layout.buf->used; ur_foreach( bi ) { // Set item word to current data value. cell = ur_ctxCell( ur_buffer( ep->bindCtxN ), 0 ); *cell = *bi.it; dc.drawCount = 0; dc.color[0] = 1.0; dc.color[1] = dc.color[2] = 0.0; dc.penY -= height; layout.it = loStart; if( ! itemview_parse( &dc, ut, &layout, (GWidget*) ep ) ) { boron_reset(ut); printf( "KR itemview_parse failed\n" ); } //printf( "KR fc %d,%d\n", dc.drawFirst, dc.drawCount ); fcBuf->ptr.i[ fcBuf->used ] = dc.drawFirst; fcBuf->ptr.i[ fcBuf->used + caOffset ] = dc.drawCount; ++fcBuf->used; dc.drawFirst += dc.drawCount; //dc.drawFirst += MAX_ITEM_TRIS; } assert( fcBuf->used == caOffset ); // Transfer vertex data to GPU. bsize = dc.drawFirst * TRI_ATTR_BYTES; if( ep->vboSize[ page ] < bsize ) { ep->vboSize[ page ] = bsize; glBufferData( GL_ARRAY_BUFFER, bsize, attr, GL_STATIC_DRAW ); } else { float* abuf = (float*) glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY ); if( abuf ) { memCpy( abuf, attr, bsize ); glUnmapBuffer( GL_ARRAY_BUFFER ); } } }
/* Sets res to either a shader! or a context! whose first value is a shader!. Returns UR_OK/UR_THROW. */ int ur_makeShader( UThread* ut, const char* vert, const char* frag, UCell* res ) { #define MAX_SHADER_PARAM 16 // LIMIT: User parameters per shader. Shader shad; Shader* sh; UIndex bufN; UBuffer* buf; int i; char name[ 128 ]; ShaderParam param[ MAX_SHADER_PARAM ]; ShaderParam* pi; GLsizei length; GLint size; GLenum type; GLint count = 0; if( ! createShader( ut, &shad, vert, frag ) ) return UR_THROW; glGetProgramiv( shad.program, GL_ACTIVE_UNIFORMS, &count ); // Collect non-gl parameters. pi = param; for( i = 0; i < count; ++i ) { glGetActiveUniform( shad.program, i, 128, &length, &size, &type, name ); if( name[0] == 'g' && name[1] == 'l' && name[2] == '_' ) continue; pi->type = type; pi->location = glGetUniformLocation( shad.program, name ); pi->name = ur_internAtom( ut, name, name + length ); if( pi->name == UR_INVALID_ATOM ) return UR_THROW; ++pi; } count = pi - param; if( count ) { ShaderParam* pend; UBuffer* ctx; UCell* cval; // We have parameters - make context to hold shader & params. ctx = ur_makeContextCell( ut, count + 1, res ); cval = ur_ctxAddWord( ctx, UR_ATOM_SHADER ); pi = param; pend = param + count; while( pi != pend ) { cval = ur_ctxAddWord( ctx, pi->name ); #if 0 printf( "KR Shader Param %d is type %d\n", (int) (pi - param), pi->type ); #endif switch( pi->type ) { case GL_FLOAT: ur_setId( cval, UT_DECIMAL ); ur_decimal( cval ) = 0.0; break; case GL_FLOAT_VEC2: case GL_FLOAT_VEC3: //case GL_FLOAT_VEC4: ur_setId( cval, UT_VEC3 ); cval->vec3.xyz[0] = cval->vec3.xyz[1] = cval->vec3.xyz[2] = 0.0f; break; case GL_INT: ur_setId( cval, UT_INT ); ur_int( cval ) = 0; break; case GL_INT_VEC2: case GL_INT_VEC3: //case GL_INT_VEC4: ur_setId( cval, UT_COORD ); break; case GL_BOOL: ur_setId( cval, UT_LOGIC ); ur_int( cval ) = 0; break; case GL_SAMPLER_2D: case GL_SAMPLER_CUBE: #ifndef GL_ES_VERSION_2_0 case GL_SAMPLER_1D: case GL_SAMPLER_3D: case GL_SAMPLER_1D_SHADOW: case GL_SAMPLER_2D_SHADOW: #endif ur_setId( cval, UT_NONE ); ur_texId(cval) = 0; // Expecting texture!. break; #ifdef GL_ES_VERSION_2_0 case GL_FLOAT_MAT4: ur_setId( cval, UT_NONE ); break; #endif default: ur_setId( cval, UT_NONE ); break; /* GL_BOOL_VEC2: GL_BOOL_VEC3: GL_BOOL_VEC4: GL_FLOAT_MAT2: GL_FLOAT_MAT3: GL_FLOAT_MAT4: GL_SAMPLER_2D_RECT: GL_SAMPLER_2D_RECT_SHADOW: */ } ++pi; } ur_ctxSort( ctx ); } ur_genBuffers( ut, 1, &bufN ); buf = ur_buffer( bufN ); buf->type = UT_SHADER; buf->ptr.v = memAlloc( sizeof(Shader) + sizeof(ShaderParam) * (count - 1) ); sh = (Shader*) buf->ptr.v; sh->program = shad.program; sh->paramCount = count; if( count ) { memCpy( sh->param, param, sizeof(ShaderParam) * count ); // First context value will be set to shader!. res = ur_buffer( res->series.buf )->ptr.cell; } ur_setId( res, UT_SHADER ); ur_setSeries( res, bufN, 0 ); return UR_OK; }
static void ledit_render( GWidget* wp ) { DrawTextState ds; GLuint* buf; GLfloat* fdata; UBuffer* str; TexFont* tf; UCell* style = glEnv.guiStyle; UThread* ut = glEnv.guiUT; EX_PTR; if( ! ep->strN ) return; tf = ur_texFontV( ut, style + CI_STYLE_EDIT_FONT ); if( tf ) { str = ur_buffer( ep->strN ); buf = vbo_bufIds( ur_buffer(ep->vboN) ); if( flagged( NEW_CURSORX ) ) { int pos; clrFlag( NEW_CURSORX ); pos = txf_charAtPixel( tf, str->ptr.b, str->ptr.b + str->used, ep->newCursorX - wp->area.x - MARGIN_L ); if( pos < 0 ) pos = (ep->newCursorX > wp->area.x + MARGIN_L) ? str->used : 0; if( pos != ep->editPos ) { ep->editPos = pos; setFlag( CHANGED ); } } #ifndef GL_ES_VERSION_2_0 glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glBindBuffer( GL_ARRAY_BUFFER, buf[0] ); if( flagged( CHANGED ) ) { clrFlag( CHANGED ); fdata = (float*) glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY ); if( fdata ) { // Cursor verticies. int xpos = txf_width(tf, str->ptr.b, str->ptr.b + ep->editPos ); // FIXME: UVs require black pixels at upper-left of texture. fdata[0] = 0.003f; fdata[1] = 0.0f; //95f; fdata[2] = (GLfloat) (wp->area.x + xpos + MARGIN_L); fdata[3] = (GLfloat) wp->area.y; fdata[4] = 0.003f; fdata[5] = 0.01f; //998f; fdata[6] = fdata[2]; fdata[7] = fdata[3] + txf_lineSpacing( tf ); //glBufferSubData(GL_ARRAY_BUFFER, 0, LEDIT_ATTR_SIZE*2, data); fdata += LEDIT_APV * 2; vbo_drawTextInit( &ds, tf, wp->area.x + MARGIN_L, wp->area.y + MARGIN_B ); ep->drawn = 6 * vbo_drawText( &ds, fdata, fdata + 2, LEDIT_APV, str->ptr.b, str->ptr.b + str->used ); glUnmapBuffer( GL_ARRAY_BUFFER ); } //printf( "KR ledit draw\n" ); } glTexCoordPointer( 2, GL_FLOAT, LEDIT_ATTR_SIZE, NULL + 0 ); glVertexPointer ( 2, GL_FLOAT, LEDIT_ATTR_SIZE, NULL + 8 ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, buf[1] ); glColor4f( 0.0, 0.0, 0.0, 1.0 ); glDrawElements( GL_TRIANGLES, ep->drawn, GL_UNSIGNED_SHORT, NULL + 4 ); if( gui_hasFocus( wp ) & GW_FOCUS_KEY ) glDrawElements( GL_LINES, 2, GL_UNSIGNED_SHORT, 0 ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); #endif } }
static void ledit_layout( GWidget* wp ) { UCell* style = glEnv.guiStyle; UThread* ut = glEnv.guiUT; UCell* rc; EX_PTR; // Set draw list variables. rc = style + CI_STYLE_LABEL; ur_setId( rc, UT_STRING ); ur_setSeries( rc, ep->strN, 0 ); rc = style + CI_STYLE_AREA; gui_initRectCoord( rc, wp, UR_ATOM_RECT ); // Compile draw lists. if( ! gDPC ) return; // Make sure the tgeo vertex buffers are bound before the switch. // Otherwise only the first case would emit the code to do it. // NOTE: This assumes the button draw programs actually use tgeo. dp_tgeoInit( gDPC ); ep->dpSwitch = dp_beginSwitch( gDPC, 2 ); rc = style + CI_STYLE_EDITOR; if( ur_is(rc, UT_BLOCK) ) ur_compileDP( ut, rc, 1 ); dp_endCase( gDPC, ep->dpSwitch ); rc = style + CI_STYLE_EDITOR_ACTIVE; if( ur_is(rc, UT_BLOCK) ) ur_compileDP( ut, rc, 1 ); dp_endCase( gDPC, ep->dpSwitch ); dp_endSwitch( gDPC, ep->dpSwitch, ep->state ); setFlag( CHANGED ); #if 0 rc = style + CI_STYLE_EDITOR_CURSOR; if( ur_is(rc, UT_BLOCK) ) { DPCompiler dc; DPCompiler* save; save = gx_beginDP( &dc ); if( save ) dc.shaderProg = save->shaderProg; ur_compileDP( ut, rc, 1 ); gx_endDP( ur_buffer( ep->textResN ), save ); //dp_compile( &dc, ut, rc->series.n ); } #endif }
static void ledit_dispatch( UThread* ut, GWidget* wp, const GLViewEvent* ev ) { EX_PTR; //printf( "KR ledit %d\n", ev->type ); switch( ev->type ) { case GLV_EVENT_BUTTON_DOWN: if( (ev->code == GLV_BUTTON_LEFT) || (ev->code == GLV_BUTTON_MIDDLE) ) { ledit_setState( ut, ep, LEDIT_STATE_EDIT ); gui_setKeyFocus( wp ); if( ev->code == GLV_BUTTON_LEFT ) { // Don't have access to font here so just notify render. ep->newCursorX = ev->x; setFlag( NEW_CURSORX ); } else // if( ev->code == GLV_BUTTON_MIDDLE ) { ledit_paste( ut, ep ); setFlag( CHANGED ); } } break; case GLV_EVENT_BUTTON_UP: case GLV_EVENT_MOTION: break; case GLV_EVENT_KEY_DOWN: if( ep->strN ) { UBuffer* str = ur_buffer( ep->strN ); if( ev->state & GLV_MASK_CTRL ) { if( ev->code == KEY_k ) { // Remove from cursor to end (from Bash). int len = str->used - ep->editPos; if( len > 0 ) { ur_arrErase( str, ep->editPos, len ); setFlag( CHANGED ); } } else if( ev->code == KEY_v ) { ledit_paste( ut, ep ); setFlag( CHANGED ); } break; } switch( ev->code ) { case KEY_Return: goto activate; case KEY_Left: if( ep->editPos > 0 ) { --ep->editPos; setFlag( CHANGED ); } break; case KEY_Right: if( ep->editPos < str->used ) { ++ep->editPos; setFlag( CHANGED ); } break; case KEY_Home: ep->editPos = 0; setFlag( CHANGED ); break; case KEY_End: ep->editPos = str->used; setFlag( CHANGED ); break; case KEY_Insert: break; case KEY_Delete: if( ep->editPos < str->used ) { ur_arrErase( str, ep->editPos, 1 ); setFlag( CHANGED ); } break; case KEY_Back_Space: if( ep->editPos > 0 ) { --ep->editPos; ur_arrErase( str, ep->editPos, 1 ); setFlag( CHANGED ); } break; default: if( str->used < ep->maxChars ) { int key = KEY_ASCII(ev); if( key >= ' ' ) { if( ep->filterN ) { UBuffer* bin = ur_buffer( ep->filterN ); if( ! _bitIsSet( bin->ptr.b, key ) ) break; } ur_arrExpand( str, ep->editPos, 1 ); if( ur_strIsUcs2(str) ) str->ptr.u16[ ep->editPos ] = key; else str->ptr.b[ ep->editPos ] = key; ++ep->editPos; setFlag( CHANGED ); } else { gui_ignoreEvent( ev ); } } break; } } break; case GLV_EVENT_KEY_UP: gui_ignoreEvent( ev ); break; case GLV_EVENT_FOCUS_IN: break; case GLV_EVENT_FOCUS_OUT: ledit_setState( ut, ep, LEDIT_STATE_DISPLAY ); break; } return; activate: if( ep->codeN ) gui_doBlockN( ut, ep->codeN ); }
/* Returns zero if matching rule not found or exception occured. */ static const UCell* _parseBin( UThread* ut, BinaryParser* pe, const UCell* rit, const UCell* rend, UIndex* spos ) { const UCell* set = 0; const UCell* tval; uint32_t bitCount; uint32_t field; UBuffer* ibin = ur_buffer( pe->inputBufN ); uint8_t* in = ibin->ptr.b + *spos; uint8_t* inEnd = ibin->ptr.b + pe->inputEnd; match: while( rit != rend ) { switch( ur_type(rit) ) { case UT_INT: bitCount = ur_int(rit); if( bitCount < 1 || bitCount > 32 ) { ur_error( PARSE_ERR, "bit-field size must be 1 to 32" ); goto parse_err; } if( bitCount > 24 ) { uint32_t high; in = pullBits( pe, bitCount - 16, in, inEnd, &high ); if( ! in ) goto failed; in = pullBits( pe, 16, in, inEnd, &field ); if( ! in ) goto failed; field |= high << 16; } else { in = pullBits( pe, bitCount, in, inEnd, &field ); if( ! in ) goto failed; } goto set_field; case UT_WORD: switch( ur_atom(rit) ) { case UR_ATOM_U8: if( in == inEnd ) goto failed; field = *in++; goto set_field; case UR_ATOM_U16: if( (inEnd - in) < 2 ) goto failed; if( pe->bigEndian ) field = (in[0] << 8) | in[1]; else field = (in[1] << 8) | in[0]; in += 2; goto set_field; case UR_ATOM_U32: if( (inEnd - in) < 4 ) goto failed; if( pe->bigEndian ) field = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | in[3]; else field = (in[3] << 24) | (in[2] << 16) | (in[1] << 8) | in[0]; in += 4; goto set_field; case UR_ATOM_SKIP: ++rit; ++in; break; #if 0 case UR_ATOM_MARK: break; case UR_ATOM_PLACE: ++rit; if( (rit != rend) && ur_is(rit, UT_WORD) ) { tval = ur_wordCell( ut, rit++ ); CHECK_WORD(tval); if( ur_is(tval, UT_BINARY) ) { pos = tval->series.it; break; } } ur_error( PARSE_ERR, "place expected series word" ); goto parse_err; #endif case UR_ATOM_COPY: // copy dest size // word! int!/word! ++rit; if( (rit != rend) && ur_is(rit, UT_WORD) ) { UCell* res = ur_wordCellM( ut, rit ); CHECK_WORD(res); if( ++rit != rend ) { tval = rit++; if( ur_is(tval, UT_WORD) ) { tval = ur_wordCell( ut, tval ); CHECK_WORD(tval); } if( ur_is(tval, UT_INT) ) { UBuffer* cb; int size = ur_int(tval); cb = ur_makeBinaryCell( ut, size, res ); cb->used = size; memCpy( cb->ptr.b, in, size ); in += size; break; } } ur_error( PARSE_ERR, "copy expected int! count" ); goto parse_err; } ur_error( PARSE_ERR, "copy expected word! destination" ); goto parse_err; case UR_ATOM_BIG_ENDIAN: ++rit; pe->bigEndian = 1; break; case UR_ATOM_LITTLE_ENDIAN: ++rit; pe->bigEndian = 0; break; default: tval = ur_wordCell( ut, rit ); CHECK_WORD(tval); if( ur_is(tval, UT_CHAR) ) goto match_char; else if( ur_is(tval, UT_STRING) ) goto match_string; else if( ur_is(tval, UT_BLOCK) ) goto match_block; /* else if( ur_is(tval, UT_BITSET) ) goto match_bitset; */ else { ur_error( PARSE_ERR, "parse expected char!/string!/block!" ); goto parse_err; } break; } break; case UT_SETWORD: set = rit++; while( (rit != rend) && ur_is(rit, UT_SETWORD) ) ++rit; break; #if 0 case UT_GETWORD: break; case UT_INT: repMin = ur_int(rit); ++rit; if( rit == rend ) return 0; if( ur_is(rit, UT_INT) ) { repMax = ur_int(rit); ++rit; } else { repMax = repMin; } goto repeat; #endif case UT_CHAR: match_char: if( *in != ur_int(rit) ) goto failed; ++in; ++rit; break; case UT_BLOCK: tval = rit; match_block: { UBlockIter bi; UIndex pos = in - ibin->ptr.b; UIndex rblkN = tval->series.buf; ur_blkSlice( ut, &bi, tval ); tval = _parseBin( ut, pe, bi.it, bi.end, &pos ); ibin = ur_buffer( pe->inputBufN ); if( ! tval ) { if( pe->exception == PARSE_EX_ERROR ) { ur_appendTrace( ut, rblkN, 0 ); return 0; } if( pe->exception == PARSE_EX_BREAK ) pe->exception = PARSE_EX_NONE; else goto failed; } in = ibin->ptr.b + pos; inEnd = ibin->ptr.b + pe->inputEnd; ++rit; } break; case UT_PAREN: { UIndex pos = in - ibin->ptr.b; if( UR_OK != pe->eval( ut, rit ) ) goto parse_err; /* Re-acquire pointer & check if input modified. */ ibin = ur_buffer( pe->inputBufN ); if( pe->sliced ) { // We have no way to track changes to the end of a slice, // so just make sure we remain in valid memery. if( ibin->used < pe->inputEnd ) pe->inputEnd = ibin->used; } else { // Not sliced, track input end. if( ibin->used != pe->inputEnd ) pe->inputEnd = ibin->used; } in = ibin->ptr.b + pos; inEnd = ibin->ptr.b + pe->inputEnd; ++rit; } break; case UT_STRING: tval = rit; match_string: { UBinaryIter bi; int size; ur_binSlice( ut, &bi, tval ); if( ur_strIsUcs2(bi.buf) ) goto bad_enc; size = bi.end - bi.it; if( size > (inEnd - in) ) goto failed; if( match_pattern_8(in, inEnd, bi.it, bi.end) == bi.end ) { in += size; ++rit; } else goto failed; } break; #if 0 case UT_BITSET: tval = rit; match_bitset: if( pos >= pe->inputEnd ) goto failed; { const UBuffer* bin = ur_bufferSer( tval ); int c = istr->ptr.c[ pos ]; if( bitIsSet( bin->ptr.b, c ) ) { ++rit; ++pos; } else goto failed; } break; #endif default: ur_error( PARSE_ERR, "invalid parse value" ); //orDatatypeName( ur_type(rit) ) ); goto parse_err; } } //complete: *spos = in - ibin->ptr.b; return rit; set_field: if( set ) { UCell* val; while( set != rit ) { val = ur_wordCellM( ut, set++ ); CHECK_WORD(val); ur_setId(val, UT_INT); ur_int(val) = field; } set = 0; } ++rit; goto match; failed: *spos = in - ibin->ptr.b; return 0; bad_enc: ur_error( ut, UR_ERR_INTERNAL, "parse binary does not handle UCS2 strings" ); //goto parse_err; parse_err: pe->exception = PARSE_EX_ERROR; return 0; }