/* ======================== idSWF::DefineShape4 ======================== */ void idSWF::DefineShape4( idSWFBitStream & bitstream ) { uint16 characterID = bitstream.ReadU16(); idSWFDictionaryEntry * entry = AddDictionaryEntry( characterID, SWF_DICT_SHAPE ); if ( entry == NULL ) { return; } idSWFShapeParser swfShapeParser; swfShapeParser.Parse( bitstream, *entry->shape, 4 ); }
/* ======================== idSWFShapeParser::ParseMorph ======================== */ void idSWFShapeParser::ParseMorph( idSWFBitStream& bitstream, idSWFShape& shape ) { extendedCount = true; lineStyle2 = false; rgba = true; morph = true; bitstream.ReadRect( shape.startBounds ); bitstream.ReadRect( shape.endBounds ); uint32 offset = bitstream.ReadU32(); // offset is the byte offset from the current read position to the 'endShape' record // we read the entire block into 'bitstream1' which moves the read pointer of 'bitstream' // to the start of the 'endShape' record idSWFBitStream bitstream1; bitstream1.Load( ( byte* )bitstream.ReadData( offset ), offset, false ); ReadFillStyle( bitstream1 ); ParseShapes( bitstream1, &bitstream, true ); TriangulateSoup( shape ); }
/* ======================== idSWFSpriteInstance::PlaceObject2 ======================== */ void idSWFSpriteInstance::PlaceObject2( idSWFBitStream & bitstream ) { c_PlaceObject2++; uint64 flags = bitstream.ReadU8(); int depth = bitstream.ReadU16(); int characterID = -1; if ( ( flags & PlaceFlagHasCharacter ) != 0 ) { characterID = bitstream.ReadU16(); } swfDisplayEntry_t * display = NULL; if ( ( flags & PlaceFlagMove ) != 0 ) { // modify an existing entry display = FindDisplayEntry( depth ); if ( display == NULL ) { idLib::Warning( "PlaceObject2: trying to modify entry %d, which doesn't exist", depth ); return; } if ( characterID >= 0 ) { // We are very picky about what kind of objects can change characters // Shapes can become other shapes, but sprites can never change if ( display->spriteInstance || display->textInstance ) { idLib::Warning( "PlaceObject2: Trying to change the character of a sprite after it's been created" ); return; } idSWFDictionaryEntry * dictEntry = sprite->swf->FindDictionaryEntry( characterID ); if ( dictEntry != NULL ) { if ( dictEntry->type == SWF_DICT_SPRITE || dictEntry->type == SWF_DICT_EDITTEXT ) { idLib::Warning( "PlaceObject2: Trying to change the character of a shape to a sprite" ); return; } } display->characterID = characterID; } } else { if ( characterID < 0 ) { idLib::Warning( "PlaceObject2: Trying to create a new object without a character" ); return; } // create a new entry display = AddDisplayEntry( depth, characterID ); if ( display == NULL ) { idLib::Warning( "PlaceObject2: trying to create a new entry at %d, but an item already exists there", depth ); return; } } if ( ( flags & PlaceFlagHasMatrix ) != 0 ) { bitstream.ReadMatrix( display->matrix ); } if ( ( flags & PlaceFlagHasColorTransform ) != 0 ) { bitstream.ReadColorXFormRGBA( display->cxf ); } if ( ( flags & PlaceFlagHasRatio ) != 0 ) { display->ratio = bitstream.ReadU16() * ( 1.0f / 65535.0f ); } if ( ( flags & PlaceFlagHasName ) != 0 ) { idStr name = bitstream.ReadString(); if ( display->spriteInstance ) { display->spriteInstance->name = name; scriptObject->Set( name, display->spriteInstance->GetScriptObject() ); } else if ( display->textInstance ) { scriptObject->Set( name, display->textInstance->GetScriptObject() ); } } if ( ( flags & PlaceFlagHasClipDepth ) != 0 ) { display->clipDepth = bitstream.ReadU16(); } if ( ( flags & PlaceFlagHasClipActions ) != 0 ) { // FIXME: clip actions } }
/* ======================== idSWFSpriteInstance::RemoveObject2 ======================== */ void idSWFSpriteInstance::RemoveObject2( idSWFBitStream & bitstream ) { RemoveDisplayEntry( bitstream.ReadU16() ); }
/* ======================== idSWFSpriteInstance::PlaceObject3 ======================== */ void idSWFSpriteInstance::PlaceObject3( idSWFBitStream & bitstream ) { c_PlaceObject3++; uint64 flags1 = bitstream.ReadU8(); uint64 flags2 = bitstream.ReadU8(); uint16 depth = bitstream.ReadU16(); if ( ( flags2 & PlaceFlagHasClassName ) != 0 || ( ( ( flags2 & PlaceFlagHasImage ) != 0 ) && ( ( flags1 & PlaceFlagHasCharacter ) != 0 ) ) ) { bitstream.ReadString(); // ignored } int characterID = -1; if ( ( flags1 & PlaceFlagHasCharacter ) != 0 ) { characterID = bitstream.ReadU16(); } swfDisplayEntry_t * display = NULL; if ( ( flags1 & PlaceFlagMove ) != 0 ) { // modify an existing entry display = FindDisplayEntry( depth ); if ( display == NULL ) { idLib::Warning( "PlaceObject3: trying to modify entry %d, which doesn't exist", depth ); return; } if ( characterID >= 0 ) { // We are very picky about what kind of objects can change characters // Shapes can become other shapes, but sprites can never change if ( display->spriteInstance || display->textInstance ) { idLib::Warning( "PlaceObject3: Trying to change the character of a sprite after it's been created" ); return; } idSWFDictionaryEntry * dictEntry = sprite->swf->FindDictionaryEntry( characterID ); if ( dictEntry != NULL ) { if ( dictEntry->type == SWF_DICT_SPRITE || dictEntry->type == SWF_DICT_EDITTEXT ) { idLib::Warning( "PlaceObject3: Trying to change the character of a shape to a sprite" ); return; } } display->characterID = characterID; } } else { if ( characterID < 0 ) { idLib::Warning( "PlaceObject3: Trying to create a new object without a character" ); return; } // create a new entry display = AddDisplayEntry( depth, characterID ); if ( display == NULL ) { idLib::Warning( "PlaceObject3: trying to create a new entry at %d, but an item already exists there", depth ); return; } } if ( ( flags1 & PlaceFlagHasMatrix ) != 0 ) { bitstream.ReadMatrix( display->matrix ); } if ( ( flags1 & PlaceFlagHasColorTransform ) != 0 ) { bitstream.ReadColorXFormRGBA( display->cxf ); } if ( ( flags1 & PlaceFlagHasRatio ) != 0 ) { display->ratio = bitstream.ReadU16() * ( 1.0f / 65535.0f ); } if ( ( flags1 & PlaceFlagHasName ) != 0 ) { idStr name = bitstream.ReadString(); if ( display->spriteInstance ) { display->spriteInstance->name = name; scriptObject->Set( name, display->spriteInstance->GetScriptObject() ); } else if ( display->textInstance ) { scriptObject->Set( name, display->textInstance->GetScriptObject() ); } } if ( ( flags1 & PlaceFlagHasClipDepth ) != 0 ) { display->clipDepth = bitstream.ReadU16(); } if ( ( flags2 & PlaceFlagHasFilterList ) != 0 ) { // we don't support filters and because the filter list is variable length we // can't support anything after the filter list either (blend modes and clip actions) idLib::Warning( "PlaceObject3: has filters" ); return; } if ( ( flags2 & PlaceFlagHasBlendMode ) != 0 ) { display->blendMode = bitstream.ReadU8(); } if ( ( flags1 & PlaceFlagHasClipActions ) != 0 ) { // FIXME: } }
/* ======================== idSWF::DefineFont2 ======================== */ void idSWF::DefineFont2( idSWFBitStream & bitstream ) { uint16 characterID = bitstream.ReadU16(); idSWFDictionaryEntry * entry = AddDictionaryEntry( characterID, SWF_DICT_FONT ); if ( entry == NULL ) { return; } uint8 flags = bitstream.ReadU8(); uint8 language = bitstream.ReadU8(); char fontName[257]; uint8 fontNameLength = bitstream.ReadU8(); memcpy( fontName, bitstream.ReadData( fontNameLength ), fontNameLength ); fontName[ fontNameLength ] = 0; entry->font->fontID = renderSystem->RegisterFont( fontName ); uint16 numGlyphs = bitstream.ReadU16(); entry->font->glyphs.SetNum( numGlyphs ); if ( flags & BIT( 3 ) ) { // 32 bit offsets uint32 offsetTableSize = ( numGlyphs + 1 ) * 4; idSWFBitStream offsetStream( bitstream.ReadData( offsetTableSize ), offsetTableSize, false ); if ( offsetStream.ReadU32() != offsetTableSize ) { idLib::Warning( "idSWF::DefineFont2: first glyph offset != offsetTableSize" ); return; } uint32 previousOffset = offsetTableSize; for ( int i = 0; i < numGlyphs; i++ ) { uint32 nextOffset = offsetStream.ReadU32(); uint32 shapeSize = nextOffset - previousOffset; previousOffset = nextOffset; idSWFBitStream shapeStream( bitstream.ReadData( shapeSize ), shapeSize, false ); idSWFShapeParser swfShapeParser; swfShapeParser.ParseFont( shapeStream, entry->font->glyphs[i] ); } } else { // 16 bit offsets uint16 offsetTableSize = ( numGlyphs + 1 ) * 2; idSWFBitStream offsetStream( bitstream.ReadData( offsetTableSize ), offsetTableSize, false ); if ( offsetStream.ReadU16() != offsetTableSize ) { idLib::Warning( "idSWF::DefineFont2: first glyph offset != offsetTableSize" ); return; } uint16 previousOffset = offsetTableSize; for ( int i = 0; i < numGlyphs; i++ ) { uint16 nextOffset = offsetStream.ReadU16(); uint16 shapeSize = nextOffset - previousOffset; previousOffset = nextOffset; idSWFBitStream shapeStream( bitstream.ReadData( shapeSize ), shapeSize, false ); idSWFShapeParser swfShapeParser; swfShapeParser.ParseFont( shapeStream, entry->font->glyphs[i] ); } } if ( flags & BIT( 2 ) ) { // 16 bit codes for ( int i = 0; i < numGlyphs; i++ ) { entry->font->glyphs[i].code = bitstream.ReadU16(); } } else { // 8 bit codes for ( int i = 0; i < numGlyphs; i++ ) { entry->font->glyphs[i].code = bitstream.ReadU8(); } } if ( flags & BIT( 7 ) ) { entry->font->ascent = bitstream.ReadS16(); entry->font->descent = bitstream.ReadS16(); entry->font->leading = bitstream.ReadS16(); for ( int i = 0; i < numGlyphs; i++ ) { entry->font->glyphs[i].advance = bitstream.ReadS16(); } for ( int i = 0; i < numGlyphs; i++ ) { swfRect_t ignored; bitstream.ReadRect( ignored ); } uint16 kearningCount = bitstream.ReadU16(); if ( flags & BIT( 2 ) ) { for ( int i = 0; i < kearningCount; i++ ) { uint16 code1 = bitstream.ReadU16(); uint16 code2 = bitstream.ReadU16(); int16 adjustment = bitstream.ReadS16(); } } else { for ( int i = 0; i < kearningCount; i++ ) { uint16 code1 = bitstream.ReadU8(); uint16 code2 = bitstream.ReadU8(); int16 adjustment = bitstream.ReadS16(); } } } }
/* ======================== idSWF::DefineEditText ======================== */ void idSWF::DefineEditText( idSWFBitStream & bitstream ) { uint16 characterID = bitstream.ReadU16(); idSWFDictionaryEntry * entry = AddDictionaryEntry( characterID, SWF_DICT_EDITTEXT ); if ( entry == NULL ) { return; } idSWFEditText * edittext = entry->edittext; bitstream.ReadRect( edittext->bounds ); bitstream.ResetBits(); bool hasText = bitstream.ReadBool(); bool wordWrap = bitstream.ReadBool(); bool multiline = bitstream.ReadBool(); bool password = bitstream.ReadBool(); bool readonly = bitstream.ReadBool(); bool hasTextColor = bitstream.ReadBool(); bool hasMaxLength = bitstream.ReadBool(); bool hasFont = bitstream.ReadBool(); bool hasFontClass = bitstream.ReadBool(); bool autoSize = bitstream.ReadBool(); bool hasLayout = bitstream.ReadBool(); bool noSelect = bitstream.ReadBool(); bool border = bitstream.ReadBool(); bool wasStatic = bitstream.ReadBool(); bool html = bitstream.ReadBool(); bool useOutlines = bitstream.ReadBool(); if ( hasFont ) { edittext->fontID = bitstream.ReadU16(); edittext->fontHeight = bitstream.ReadU16(); } if ( hasFontClass ) { idStr fontClass = bitstream.ReadString(); } if ( hasTextColor ) { bitstream.ReadColorRGBA( edittext->color ); } if ( hasMaxLength ) { edittext->maxLength = bitstream.ReadU16(); } if ( hasLayout ) { edittext->align = (swfEditTextAlign_t)bitstream.ReadU8(); edittext->leftMargin = bitstream.ReadU16(); edittext->rightMargin = bitstream.ReadU16(); edittext->indent = bitstream.ReadU16(); edittext->leading = bitstream.ReadS16(); } edittext->variable = bitstream.ReadString(); if ( hasText ) { const char * text = bitstream.ReadString(); idStr initialText; // convert html tags if necessary for ( int i = 0; text[i] != 0; i++ ) { if ( text[i] == '<' ) { if ( i != 0 && text[i+1] == 'p' ) { initialText.Append( '\n' ); } for ( ; text[i] != 0 && text[i] != '>'; i++ ) { } continue; } byte tc = (byte)text[i]; if ( tc == '&' ) { idStr special; for ( i++; text[i] != 0 && text[i] != ';'; i++ ) { special.Append( text[i] ); } if ( special.Icmp( "amp" ) == 0 ) { tc = '&'; } else if ( special.Icmp( "apos" ) == 0 ) { tc = '\''; } else if ( special.Icmp( "lt" ) == 0 ) { tc = '<'; } else if ( special.Icmp( "gt" ) == 0 ) { tc = '>'; } else if ( special.Icmp( "quot" ) == 0 ) { tc = '\"'; } } initialText.Append( tc ); } edittext->initialText = initialText; } edittext->flags |= wordWrap ? SWF_ET_WORDWRAP : 0; edittext->flags |= multiline ? SWF_ET_MULTILINE : 0; edittext->flags |= password ? SWF_ET_PASSWORD : 0; edittext->flags |= readonly ? SWF_ET_READONLY : 0; edittext->flags |= autoSize ? SWF_ET_AUTOSIZE : 0; edittext->flags |= border ? SWF_ET_BORDER : 0; }
/* ======================== idSWF::DefineTextX ======================== */ void idSWF::DefineTextX( idSWFBitStream & bitstream, bool rgba ) { uint16 characterID = bitstream.ReadU16(); idSWFDictionaryEntry * entry = AddDictionaryEntry( characterID, SWF_DICT_TEXT ); if ( entry == NULL ) { return; } idSWFText * text = entry->text; bitstream.ReadRect( text->bounds ); bitstream.ReadMatrix( text->matrix ); uint8 glyphBits = bitstream.ReadU8(); uint8 advanceBits = bitstream.ReadU8(); while ( true ) { uint8 flags = bitstream.ReadU8(); if ( flags == 0 ) { break; } idSWFTextRecord & textRecord = text->textRecords.Alloc(); if ( flags & BIT( 3 ) ) { textRecord.fontID = bitstream.ReadU16(); } if ( flags & BIT( 2 ) ) { if ( rgba ) { bitstream.ReadColorRGBA( textRecord.color ); } else { bitstream.ReadColorRGB( textRecord.color ); } } if ( flags & BIT( 0 ) ) { textRecord.xOffset = bitstream.ReadS16(); } if ( flags & BIT( 1 ) ) { textRecord.yOffset = bitstream.ReadS16(); } if ( flags & BIT( 3 ) ) { textRecord.textHeight = bitstream.ReadU16(); } textRecord.firstGlyph = text->glyphs.Num(); textRecord.numGlyphs = bitstream.ReadU8(); for ( int i = 0; i < textRecord.numGlyphs; i++ ) { swfGlyphEntry_t & glyph = text->glyphs.Alloc(); glyph.index = bitstream.ReadU( glyphBits ); glyph.advance = bitstream.ReadS( advanceBits ); } }; }
/* ======================== idSWFSpriteInstance::DoAction ======================== */ void idSWFSpriteInstance::DoAction( idSWFBitStream & bitstream ) { swfAction_t & action = actions.Alloc(); action.data = bitstream.ReadData( bitstream.Length() ); action.dataLength = bitstream.Length(); }
/* ======================== idSWFShapeParser::ParseShape ======================== */ void idSWFShapeParser::Parse( idSWFBitStream& bitstream, idSWFShape& shape, int recordType ) { extendedCount = ( recordType > 1 ); lineStyle2 = ( recordType == 4 ); rgba = ( recordType >= 3 ); morph = false; bitstream.ReadRect( shape.startBounds ); shape.endBounds = shape.startBounds; if( recordType == 4 ) { swfRect_t edgeBounds; bitstream.ReadRect( edgeBounds ); bitstream.ReadU8(); // flags (that we ignore) } ReadFillStyle( bitstream ); ParseShapes( bitstream, NULL, false ); TriangulateSoup( shape ); shape.lineDraws.SetNum( lineDraws.Num() ); for( int i = 0; i < lineDraws.Num(); i++ ) { idSWFShapeDrawLine& ld = shape.lineDraws[i]; swfSPDrawLine_t& spld = lineDraws[i]; ld.style = spld.style; ld.indices.SetNum( spld.edges.Num() * 3 ); ld.indices.SetNum( 0 ); for( int e = 0; e < spld.edges.Num(); e++ ) { int v0 = ld.startVerts.AddUnique( verts[ spld.edges[e].start.v0 ] ); ld.indices.Append( v0 ); ld.indices.Append( v0 ); // Rather then tesselating curves at run time, we do them once here by inserting a vert every 10 units // It may not wind up being 10 actual pixels when rendered because the shape may have scaling applied to it if( spld.edges[e].start.cp != 0xFFFF ) { assert( spld.edges[e].end.cp != 0xFFFF ); float length1 = ( verts[ spld.edges[e].start.v0 ] - verts[ spld.edges[e].start.v1 ] ).Length(); float length2 = ( verts[ spld.edges[e].end.v0 ] - verts[ spld.edges[e].end.v1 ] ).Length(); int numPoints = 1 + idMath::Ftoi( Max( length1, length2 ) / 10.0f ); for( int ti = 0; ti < numPoints; ti++ ) { float t0 = ( ti + 1 ) / ( ( float ) numPoints + 1.0f ); float t1 = ( 1.0f - t0 ); float c1 = t1 * t1; float c2 = t0 * t1 * 2.0f; float c3 = t0 * t0; idVec2 p1 = c1 * verts[ spld.edges[e].start.v0 ]; p1 += c2 * verts[ spld.edges[e].start.cp ]; p1 += c3 * verts[ spld.edges[e].start.v1 ]; int v1 = ld.startVerts.AddUnique( p1 ); ld.indices.Append( v1 ); ld.indices.Append( v1 ); ld.indices.Append( v1 ); } } ld.indices.Append( ld.startVerts.AddUnique( verts[ spld.edges[e].start.v1 ] ) ); } } }
/* ======================== idSWFShapeParser::ParseShapes ======================== */ void idSWFShapeParser::ParseShapes( idSWFBitStream& bitstream1, idSWFBitStream* bitstream2, bool swap ) { int32 pen1X = 0; int32 pen1Y = 0; int32 pen2X = 0; int32 pen2Y = 0; uint8 fillStyle0 = 0; uint8 fillStyle1 = 0; uint8 lineStyle = 0; uint16 baseFillStyle = 0; uint16 baseLineStyle = 0; uint8 numBits = bitstream1.ReadU8(); uint8 numFillBits1 = numBits >> 4; uint8 numLineBits1 = numBits & 0xF; uint8 numFillBits2 = 0; uint8 numLineBits2 = 0; if( bitstream2 ) { numBits = bitstream2->ReadU8(); numFillBits2 = numBits >> 4; numLineBits2 = numBits & 0xF; } while( true ) { if( !bitstream1.ReadBool() ) { bool stateNewStyles = bitstream1.ReadBool(); bool stateLineStyle = bitstream1.ReadBool(); bool stateFillStyle1 = bitstream1.ReadBool(); bool stateFillStyle0 = bitstream1.ReadBool(); bool stateMoveTo = bitstream1.ReadBool(); if( ( stateNewStyles || stateLineStyle || stateFillStyle1 || stateFillStyle0 || stateMoveTo ) == false ) { // end record if( bitstream2 ) { uint8 flags = bitstream2->ReadU( 6 ); if( flags != 0 ) { idLib::Warning( "idSWFShapeParser: morph stream 1 ends before 2" ); break; } } break; } if( stateMoveTo ) { uint8 moveBits = bitstream1.ReadU( 5 ); pen1X = bitstream1.ReadS( moveBits ); pen1Y = bitstream1.ReadS( moveBits ); } if( stateFillStyle0 ) { fillStyle0 = bitstream1.ReadU( numFillBits1 ); } if( stateFillStyle1 ) { fillStyle1 = bitstream1.ReadU( numFillBits1 ); } if( stateLineStyle ) { lineStyle = bitstream1.ReadU( numLineBits1 ); } if( stateNewStyles ) { baseFillStyle = fillDraws.Num(); baseLineStyle = lineDraws.Num(); ReadFillStyle( bitstream1 ); numBits = bitstream1.ReadU8(); numFillBits1 = numBits >> 4; numLineBits1 = numBits & 0xF; } if( bitstream2 ) { bool isEdge = bitstream2->ReadBool(); if( isEdge ) { idLib::Warning( "idSWFShapeParser: morph stream 1 defines style change, but stream 2 does not" ); break; } bool stateNewStyles = bitstream2->ReadBool(); bool stateLineStyle = bitstream2->ReadBool(); bool stateFillStyle1 = bitstream2->ReadBool(); bool stateFillStyle0 = bitstream2->ReadBool(); bool stateMoveTo = bitstream2->ReadBool(); if( stateMoveTo ) { uint8 moveBits = bitstream2->ReadU( 5 ); pen2X = bitstream2->ReadS( moveBits ); pen2Y = bitstream2->ReadS( moveBits ); } if( stateFillStyle0 ) { if( bitstream2->ReadU( numFillBits2 ) != fillStyle0 ) { idLib::Warning( "idSWFShapeParser: morph stream 2 defined a different fillStyle0 from stream 1" ); break; } } if( stateFillStyle1 ) { if( bitstream2->ReadU( numFillBits2 ) != fillStyle1 ) { idLib::Warning( "idSWFShapeParser: morph stream 2 defined a different fillStyle1 from stream 1" ); break; } } if( stateLineStyle ) { if( bitstream2->ReadU( numLineBits2 ) != lineStyle ) { idLib::Warning( "idSWFShapeParser: morph stream 2 defined a different lineStyle from stream 1" ); break; } } if( stateNewStyles ) { idLib::Warning( "idSWFShapeParser: morph stream 2 defines new styles" ); break; } } } else {
/* =================== idSWF::SetBackgroundColor =================== */ void idSWF::SetBackgroundColor( idSWFBitStream& bitstream ) { bitstream.Seek( 4 ); // int }
/* =================== idSWF::Metadata =================== */ void idSWF::Metadata( idSWFBitStream& bitstream ) { bitstream.ReadString(); // XML string }
/* =================== idSWF::FileAttributes Extra data that won't fit in a SWF header =================== */ void idSWF::FileAttributes( idSWFBitStream& bitstream ) { bitstream.Seek( 5 ); // 5 booleans }