Coverage Report

Created: 2026-10-16 07:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/scenegraph/commands.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2012
6
 *          All rights reserved
7
 *
8
 *  This file is part of GPAC / Scene Graph sub-project
9
 *
10
 *  GPAC is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU Lesser General Public License as published by
12
 *  the Free Software Foundation; either version 2, or (at your option)
13
 *  any later version.
14
 *
15
 *  GPAC is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU Lesser General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU Lesser General Public
21
 *  License along with this library; see the file COPYING.  If not, write to
22
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
 *
24
 */
25
26
27
#include <gpac/internal/scenegraph_dev.h>
28
/*MPEG4 tags (for internal nodes)*/
29
#include <gpac/nodes_mpeg4.h>
30
31
#include <gpac/internal/laser_dev.h>
32
33
34
GF_EXPORT
35
GF_Command *gf_sg_command_new(GF_SceneGraph *graph, u32 tag)
36
0
{
37
0
  GF_Command *ptr;
38
0
  GF_SAFEALLOC(ptr, GF_Command);
39
0
  if (!ptr) return NULL;
40
0
  ptr->tag = tag;
41
0
  ptr->in_scene = graph;
42
0
  ptr->command_fields = gf_list_new();
43
0
  if (graph) {
44
0
    if (!graph->referencing_commands)
45
0
      graph->referencing_commands = gf_list_new();
46
0
    gf_list_add(graph->referencing_commands, &ptr->in_scene);
47
0
  }
48
0
  if (tag < GF_SG_LAST_BIFS_COMMAND) ptr->new_proto_list = gf_list_new();
49
0
  return ptr;
50
0
}
51
52
GF_EXPORT
53
void gf_sg_command_del(GF_Command *com)
54
0
{
55
0
#ifndef GPAC_DISABLE_VRML
56
0
  u32 i;
57
0
  GF_Proto *proto;
58
0
#endif
59
0
  if (!com) return;
60
61
0
  if (com->in_scene && com->in_scene->referencing_commands)
62
0
    gf_list_del_item(com->in_scene->referencing_commands, &com->in_scene);
63
64
  /* deregister from the node's back-reference list before the command struct is freed;
65
     if gf_node_free already ran (force-free in gf_sg_reset), com->node will be NULL here */
66
0
  if (com->node && com->node->sgprivate && com->node->sgprivate->referencing_commands)
67
0
    gf_list_del_item(com->node->sgprivate->referencing_commands, &com->node);
68
69
70
0
  if (com->tag < GF_SG_LAST_BIFS_COMMAND) {
71
0
#ifndef GPAC_DISABLE_VRML
72
0
    while (gf_list_count(com->command_fields)) {
73
0
      GF_CommandField *inf = (GF_CommandField *)gf_list_get(com->command_fields, 0);
74
0
      gf_list_rem(com->command_fields, 0);
75
76
0
      switch (inf->fieldType) {
77
0
      case GF_SG_VRML_SFNODE:
78
0
        if (inf->new_node) gf_node_try_destroy(com->in_scene, inf->new_node, NULL);
79
0
        break;
80
0
      case GF_SG_VRML_MFNODE:
81
0
        if (inf->field_ptr) {
82
0
          GF_ChildNodeItem *child;
83
0
          child = inf->node_list;
84
0
          while (child) {
85
0
            GF_ChildNodeItem *cur = child;
86
0
            gf_node_try_destroy(com->in_scene, child->node, NULL);
87
0
            child = child->next;
88
0
            gf_free(cur);
89
0
          }
90
0
        }
91
0
        break;
92
0
      default:
93
0
        if (inf->field_ptr && inf->field_ptr != (void *)&inf->new_node  && inf->field_ptr != (void *)&inf->node_list) {
94
0
          gf_sg_vrml_field_pointer_del(inf->field_ptr, inf->fieldType);
95
0
        }
96
0
        if (inf->new_node) gf_node_try_destroy(com->in_scene, inf->new_node, NULL);
97
0
        break;
98
0
      }
99
0
      gf_free(inf);
100
0
    }
101
0
#endif
102
0
  } else {
103
0
#ifndef GPAC_DISABLE_SVG
104
0
    while (gf_list_count(com->command_fields)) {
105
0
      GF_CommandField *inf = (GF_CommandField *)gf_list_get(com->command_fields, 0);
106
0
      gf_list_rem(com->command_fields, 0);
107
108
0
      if (inf->new_node)
109
0
        gf_node_try_destroy(com->in_scene, inf->new_node, NULL);
110
0
      else if (inf->node_list) {
111
0
        GF_ChildNodeItem *child;
112
0
        child = inf->node_list;
113
0
        while (child) {
114
0
          GF_ChildNodeItem *cur = child;
115
0
          gf_node_try_destroy(com->in_scene, child->node, NULL);
116
0
          child = child->next;
117
0
          gf_free(cur);
118
0
        }
119
0
      } else if (inf->field_ptr) {
120
0
        gf_svg_delete_attribute_value(inf->fieldType, inf->field_ptr, com->in_scene);
121
0
      }
122
0
      gf_free(inf);
123
0
    }
124
0
#endif
125
0
  }
126
0
  gf_list_del(com->command_fields);
127
128
0
#ifndef GPAC_DISABLE_VRML
129
0
  i=0;
130
0
  while ((proto = (GF_Proto*)gf_list_enum(com->new_proto_list, &i))) {
131
0
    gf_sg_proto_del(proto);
132
0
  }
133
0
  gf_list_del(com->new_proto_list);
134
0
#endif
135
136
0
  if (com->node) {
137
0
    gf_node_try_destroy(com->in_scene, com->node, NULL);
138
0
  }
139
140
0
  if (com->del_proto_list) gf_free(com->del_proto_list);
141
0
  if (com->def_name) gf_free(com->def_name);
142
0
  if (com->scripts_to_load) gf_list_del(com->scripts_to_load);
143
0
  if (com->unres_name) gf_free(com->unres_name);
144
0
  gf_free(com);
145
0
}
146
147
static void SG_CheckFieldChange(GF_Node *node, GF_FieldInfo *field)
148
0
{
149
  /*and propagate eventIn if any*/
150
0
  if (field->on_event_in) {
151
0
    field->on_event_in(node, NULL);
152
0
  }
153
0
#ifndef GPAC_DISABLE_VRML
154
0
  else if ((field->eventType==GF_SG_EVENT_IN) && (gf_node_get_tag(node) == TAG_MPEG4_Script)) {
155
0
    gf_sg_script_event_in(node, field);
156
0
  }
157
0
  else {
158
    /*Notify eventOut in all cases to handle protos*/
159
0
    gf_node_event_out(node, field->fieldIndex);
160
0
  }
161
0
#endif
162
  /*signal node modif*/
163
0
  gf_node_changed(node, field);
164
0
}
165
166
#ifndef GPAC_DISABLE_SVG
167
static void gf_node_unregister_children_deactivate(GF_Node *container, GF_ChildNodeItem *child)
168
0
{
169
0
  while (child) {
170
0
    GF_ChildNodeItem *cur;
171
0
    gf_node_deactivate(child->node);
172
0
    gf_node_unregister(child->node, container);
173
0
    cur = child;
174
0
    child = child->next;
175
0
    gf_free(cur);
176
0
  }
177
0
}
178
#endif
179
180
181
GF_EXPORT
182
GF_Err gf_sg_command_apply(GF_SceneGraph *graph, GF_Command *com, Double time_offset)
183
0
{
184
0
  GF_Err e;
185
0
#if !defined(GPAC_DISABLE_VRML) || !defined(GPAC_DISABLE_SVG)
186
0
  GF_CommandField *inf;
187
0
  GF_Node *node;
188
0
#endif
189
190
0
#ifndef GPAC_DISABLE_VRML
191
0
  GF_FieldInfo field;
192
0
  void *slot_ptr;
193
0
  GF_Node *def;
194
0
#endif
195
196
0
  if (!com || !graph) return GF_BAD_PARAM;
197
198
0
  if (com->never_apply) return GF_OK;
199
200
0
  e = GF_OK;
201
0
  switch (com->tag) {
202
0
#ifndef GPAC_DISABLE_VRML
203
0
  case GF_SG_SCENE_REPLACE:
204
    // proto instances own their private namespace, any other foreign graph (proto code) dangles once protos are destroyed below
205
0
    if (com->node && (com->node->sgprivate->scenegraph != graph)
206
0
        && !((com->node->sgprivate->tag == TAG_ProtoNode) && (com->node->sgprivate->scenegraph->parent_scene == graph))) {
207
0
      return GF_NON_COMPLIANT_BITSTREAM;
208
0
    }
209
    /*unregister root*/
210
0
    gf_node_unregister(graph->RootNode, NULL);
211
    /*remove all protos and routes*/
212
0
    while (gf_list_count(graph->routes_to_activate))
213
0
      gf_list_rem(graph->routes_to_activate, 0);
214
215
0
    if (!com->aggregated) {
216
      /*destroy all routes*/
217
0
      while (gf_list_count(graph->Routes)) {
218
0
        GF_Route *r = (GF_Route *)gf_list_get(graph->Routes, 0);
219
        /*this will unregister the route from the graph, so don't delete the chain entry*/
220
0
        gf_sg_route_del(r);
221
0
      }
222
      /* flush extern-proto links before freeing protos to avoid dangling
223
         GF_ProtoLink::url pointers (mirrors gf_sg_reset) */
224
0
      if (!graph->pOwningProto && gf_list_count(graph->protos) && graph->GetExternProtoLib)
225
0
        graph->GetExternProtoLib(graph->userpriv, NULL);
226
      /*destroy all proto*/
227
0
      while (gf_list_count(graph->protos)) {
228
0
        GF_Proto *p = (GF_Proto*)gf_list_get(graph->protos, 0);
229
        /*this will unregister the proto from the graph, so don't delete the chain entry*/
230
0
        gf_sg_proto_del(p);
231
0
      }
232
0
    }
233
    /*DO NOT TOUCH node registry*/
234
    /*DO NOT TOUCH UNREGISTERED PROTOS*/
235
236
    /*if no protos (previously aggregated command) create proto list*/
237
0
    if (!graph->protos) graph->protos = gf_list_new();
238
239
    /*move all protos in graph*/
240
0
    while (gf_list_count(com->new_proto_list)) {
241
0
      GF_Proto *p = (GF_Proto*)gf_list_get(com->new_proto_list, 0);
242
0
      gf_list_rem(com->new_proto_list, 0);
243
0
      gf_list_del_item(graph->unregistered_protos, p);
244
0
      gf_list_add(graph->protos, p);
245
0
    }
246
    /*assign new root (no need to register/unregister)*/
247
0
    graph->RootNode = com->node;
248
0
    gf_sg_command_set_node(com, NULL);
249
0
    break;
250
251
0
  case GF_SG_NODE_REPLACE:
252
0
    if (!gf_list_count(com->command_fields)) return GF_OK;
253
0
    inf = (GF_CommandField*)gf_list_get(com->command_fields, 0);
254
0
    e = gf_node_replace(com->node, inf->new_node, 0);
255
    //TOCHECK - this is commented as registering shouldn't happen (already done at command creation) and creates mem leak
256
//    if (inf->new_node) gf_node_register(inf->new_node, NULL);
257
0
    break;
258
259
0
  case GF_SG_MULTIPLE_REPLACE:
260
0
  case GF_SG_FIELD_REPLACE:
261
0
  {
262
0
    u32 j;
263
0
    GF_ChildNodeItem *list, *cur, *prev;
264
0
    GF_ChildNodeItem single_item;
265
0
    j=0;
266
0
    while ((inf = (GF_CommandField*)gf_list_enum(com->command_fields, &j))) {
267
0
      e = gf_node_get_field(com->node, inf->fieldIndex, &field);
268
0
      if (e) return e;
269
270
0
      switch (field.fieldType) {
271
0
      case GF_SG_VRML_SFNODE:
272
0
      {
273
0
        node = *((GF_Node **) field.far_ptr);
274
0
        e = gf_node_unregister(node, com->node);
275
0
        *((GF_Node **) field.far_ptr) = inf->new_node;
276
0
        if (!e) gf_node_register(inf->new_node, com->node);
277
0
        break;
278
0
      }
279
0
      case GF_SG_VRML_MFNODE:
280
0
        gf_node_unregister_children(com->node, * ((GF_ChildNodeItem **) field.far_ptr));
281
0
        * ((GF_ChildNodeItem **) field.far_ptr) = NULL;
282
0
        if (!inf->field_ptr) break;
283
        // field_ptr aliases inf->node_list, except for a single node value where it aliases inf->new_node
284
0
        if (inf->field_ptr == (void*)&inf->node_list) {
285
0
          list = inf->node_list;
286
0
        } else if ((inf->field_ptr == (void*)&inf->new_node) && inf->new_node) {
287
0
          single_item.node = inf->new_node;
288
0
          single_item.next = NULL;
289
0
          list = &single_item;
290
0
        } else {
291
0
          break;
292
0
        }
293
0
        prev=NULL;
294
0
        while (list) {
295
0
          cur = gf_malloc(sizeof(GF_ChildNodeItem));
296
0
          cur->next = NULL;
297
0
          cur->node = list->node;
298
0
          if (prev) {
299
0
            prev->next = cur;
300
0
          } else {
301
0
            * ((GF_ChildNodeItem **) field.far_ptr) = cur;
302
0
          }
303
0
          gf_node_register(list->node, com->node);
304
0
          prev = cur;
305
0
          list = list->next;
306
0
        }
307
0
        break;
308
0
      case GF_SG_VRML_SFCOMMANDBUFFER:
309
0
      {
310
0
        u32 i, count;
311
0
        GF_SceneGraph *sg;
312
0
        SFCommandBuffer *cb_dst = (SFCommandBuffer *)field.far_ptr;
313
0
        SFCommandBuffer *cb_src = (SFCommandBuffer *)inf->field_ptr;
314
315
        /*reset dest*/
316
0
        if (!cb_dst->commandList) cb_dst->commandList = gf_list_new();
317
318
0
        while (gf_list_count(cb_dst->commandList)) {
319
0
          GF_Command *sub_com = (GF_Command *)gf_list_get(cb_dst->commandList, 0);
320
0
          gf_sg_command_del(sub_com);
321
0
          gf_list_rem(cb_dst->commandList, 0);
322
0
        }
323
0
        if (cb_dst->buffer) {
324
0
          gf_free(cb_dst->buffer);
325
0
          cb_dst->buffer = NULL;
326
0
        }
327
328
        /*clone command list*/
329
0
        sg = gf_node_get_graph(com->node);
330
0
        count = gf_list_count(cb_src->commandList);
331
0
        for (i=0; i<count; i++) {
332
0
          GF_Command *sub_com = (GF_Command *)gf_list_get(cb_src->commandList, i);
333
0
          GF_Command *new_com = gf_sg_vrml_command_clone(sub_com, sg, 0);
334
0
          gf_list_add(cb_dst->commandList, new_com);
335
0
        }
336
0
      }
337
0
      break;
338
339
0
      default:
340
        /*this is a regular field, reset it and clone - we cannot switch pointers since the
341
        original fields are NOT pointers*/
342
0
        if (!gf_sg_vrml_is_sf_field(field.fieldType)) {
343
0
          e = gf_sg_vrml_mf_reset(field.far_ptr, field.fieldType);
344
0
        }
345
0
        if (e) return e;
346
0
        gf_sg_vrml_field_copy(field.far_ptr, inf->field_ptr, field.fieldType);
347
348
0
        if ((field.fieldType==GF_SG_VRML_SFTIME) && !strstr(field.name, "media"))
349
0
          *(SFTime *)field.far_ptr = *(SFTime *)field.far_ptr + time_offset;
350
0
        break;
351
0
      }
352
0
      SG_CheckFieldChange(com->node, &field);
353
0
    }
354
0
    break;
355
0
  }
356
357
0
  case GF_SG_MULTIPLE_INDEXED_REPLACE:
358
0
  case GF_SG_INDEXED_REPLACE:
359
0
  {
360
0
    u32 sftype, i=0;
361
0
    while ((inf = (GF_CommandField*)gf_list_enum(com->command_fields, &i))) {
362
0
      e = gf_node_get_field(com->node, inf->fieldIndex, &field);
363
0
      if (e) return e;
364
365
      /*if MFNode remove the child and set new node*/
366
0
      if (field.fieldType == GF_SG_VRML_MFNODE) {
367
        /*we must remove the node before in case the new node uses the same ID (not forbidden) and this
368
        command removes the last instance of the node with the same ID*/
369
0
        gf_node_replace_child(com->node, (GF_ChildNodeItem**) field.far_ptr, inf->pos, inf->new_node);
370
0
        if (inf->new_node) gf_node_register(inf->new_node, NULL);
371
0
      }
372
      /*erase the field item*/
373
0
      else {
374
0
        if (!field.far_ptr) return GF_NON_COMPLIANT_BITSTREAM;
375
0
        if ((inf->pos < 0) || ((u32) inf->pos >= ((GenMFField *) field.far_ptr)->count) ) {
376
0
          inf->pos = ((GenMFField *)field.far_ptr)->count - 1;
377
          /*may happen with text and default value*/
378
0
          if (inf->pos < 0) {
379
0
            inf->pos = 0;
380
0
            gf_sg_vrml_mf_alloc(field.far_ptr, field.fieldType, 1);
381
0
          }
382
0
        }
383
0
        e = gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, & slot_ptr, inf->pos);
384
0
        if (e) return e;
385
0
        sftype = gf_sg_vrml_get_sf_type(field.fieldType);
386
0
        gf_sg_vrml_field_copy(slot_ptr, inf->field_ptr, sftype);
387
        /*note we don't add time offset, since there's no MFTime*/
388
0
      }
389
0
      SG_CheckFieldChange(com->node, &field);
390
0
    }
391
0
    break;
392
0
  }
393
0
  case GF_SG_ROUTE_REPLACE:
394
0
  {
395
0
    GF_Route *r;
396
0
    char *name;
397
0
    r = gf_sg_route_find(graph, com->RouteID);
398
0
    def = gf_sg_find_node(graph, com->fromNodeID);
399
0
    node = gf_sg_find_node(graph, com->toNodeID);
400
0
    if (!node || !def) return GF_SG_UNKNOWN_NODE;
401
0
    name = NULL;
402
0
    if (r) {
403
0
      name = r->name;
404
0
      r->name = NULL;
405
0
      gf_sg_route_del(r);
406
0
    }
407
0
    r = gf_sg_route_new(graph, def, com->fromFieldIndex, node, com->toFieldIndex);
408
0
    gf_sg_route_set_id(r, com->RouteID);
409
0
    if (name) {
410
0
      gf_sg_route_set_name(r, name);
411
0
      gf_free(name);
412
0
    }
413
0
    break;
414
0
  }
415
0
  case GF_SG_NODE_DELETE_EX:
416
0
  case GF_SG_NODE_DELETE:
417
0
  {
418
0
    if (com->node) gf_node_replace(com->node, NULL, (com->tag==GF_SG_NODE_DELETE_EX) ? 1 : 0);
419
0
    break;
420
0
  }
421
0
  case GF_SG_ROUTE_DELETE:
422
0
  {
423
0
    return gf_sg_route_del_by_id(graph, com->RouteID);
424
0
  }
425
0
  case GF_SG_INDEXED_DELETE:
426
0
  {
427
0
    if (!gf_list_count(com->command_fields)) return GF_OK;
428
0
    inf = (GF_CommandField*)gf_list_get(com->command_fields, 0);
429
430
0
    e = gf_node_get_field(com->node, inf->fieldIndex, &field);
431
0
    if (e) return e;
432
0
    if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM;
433
434
    /*then we need special handling in case of a node*/
435
0
    if (gf_sg_vrml_get_sf_type(field.fieldType) == GF_SG_VRML_SFNODE) {
436
0
      e = gf_node_replace_child(com->node, (GF_ChildNodeItem **) field.far_ptr, inf->pos, NULL);
437
0
    } else {
438
0
      if (!field.far_ptr) return GF_NON_COMPLIANT_BITSTREAM;
439
0
      if ((inf->pos < 0) || ((u32) inf->pos >= ((GenMFField *) field.far_ptr)->count) ) {
440
0
        inf->pos = ((GenMFField *)field.far_ptr)->count - 1;
441
0
      }
442
      /*this is a regular MFField, just remove the item (gf_realloc)*/
443
0
      e = gf_sg_vrml_mf_remove(field.far_ptr, field.fieldType, inf->pos);
444
0
    }
445
    /*deletion -> node has changed*/
446
0
    if (!e) SG_CheckFieldChange(com->node, &field);
447
0
    break;
448
0
  }
449
0
  case GF_SG_NODE_INSERT:
450
0
  {
451
0
    if (!gf_list_count(com->command_fields)) return GF_OK;
452
0
    inf = (GF_CommandField*)gf_list_get(com->command_fields, 0);
453
454
0
    if (!com->node || !gf_sg_mpeg4_node_get_child_ndt(com->node))
455
0
      return GF_NON_COMPLIANT_BITSTREAM;
456
457
0
    e = gf_node_insert_child(com->node, inf->new_node, inf->pos);
458
0
    if (!e) e = gf_node_register(inf->new_node, com->node);
459
0
    if (!e) {
460
0
      gf_node_event_out(com->node, inf->fieldIndex);
461
0
      gf_node_changed(com->node, NULL);
462
0
    }
463
0
    break;
464
0
  }
465
0
  case GF_SG_ROUTE_INSERT:
466
0
  {
467
0
    GF_Route *r;
468
0
    def = gf_sg_find_node(graph, com->fromNodeID);
469
0
    node = gf_sg_find_node(graph, com->toNodeID);
470
0
    if (!node || !def) return GF_SG_UNKNOWN_NODE;
471
0
    r = gf_sg_route_new(graph, def, com->fromFieldIndex, node, com->toFieldIndex);
472
0
    if (com->RouteID) gf_sg_route_set_id(r, com->RouteID);
473
0
    if (com->def_name) {
474
0
      gf_sg_route_set_name(r, com->def_name);
475
0
      gf_free(com->def_name);
476
0
      com->def_name = NULL;
477
0
    }
478
0
    break;
479
0
  }
480
0
  case GF_SG_INDEXED_INSERT:
481
0
  {
482
0
    u32 sftype;
483
0
    if (!gf_list_count(com->command_fields)) return GF_OK;
484
0
    inf = (GF_CommandField*)gf_list_get(com->command_fields, 0);
485
0
    e = gf_node_get_field(com->node, inf->fieldIndex, &field);
486
0
    if (e) return e;
487
488
    /*rescale the MFField and parse the SFField*/
489
0
    if (field.fieldType != GF_SG_VRML_MFNODE) {
490
0
      if (inf->pos == -1) {
491
0
        e = gf_sg_vrml_mf_append(field.far_ptr, field.fieldType, & slot_ptr);
492
0
      } else {
493
0
        e = gf_sg_vrml_mf_insert(field.far_ptr, field.fieldType, & slot_ptr, inf->pos);
494
0
      }
495
0
      if (e) return e;
496
0
      sftype = gf_sg_vrml_get_sf_type(field.fieldType);
497
0
      gf_sg_vrml_field_copy(slot_ptr, inf->field_ptr, sftype);
498
0
    } else {
499
0
      if (inf->new_node) {
500
0
        if (inf->pos == -1) {
501
0
          gf_node_list_add_child( (GF_ChildNodeItem **) field.far_ptr, inf->new_node);
502
0
        } else {
503
0
          gf_node_list_insert_child((GF_ChildNodeItem **) field.far_ptr, inf->new_node, inf->pos);
504
0
        }
505
0
        gf_node_register(inf->new_node, com->node);
506
0
      }
507
0
    }
508
0
    if (!e) SG_CheckFieldChange(com->node, &field);
509
0
    break;
510
0
  }
511
0
  case GF_SG_PROTO_INSERT:
512
    /*promote newly-declared protos from this command into the target graph,
513
      but only those we can still verify are live: xmt_parse_proto/gf_sg_proto_new
514
      file every fresh proto in graph->unregistered_protos alongside the command's
515
      new_proto_list, so a pointer that is NOT in unregistered_protos was either
516
      (a) already promoted by a peer PROTO_INSERT, or (b) freed by an earlier
517
      PROTO_DELETE. Either way, handing it to graph->protos would leave a stale
518
      pointer that a later PROTO_DELETE_ALL walk would deref at
519
      scenegraph/vrml_proto.c:108 (heap-use-after-free). gf_list_del_item only
520
      compares pointer identity, so this check is safe on a dangling p.*/
521
0
    while (gf_list_count(com->new_proto_list)) {
522
0
      GF_Proto *p = (GF_Proto*)gf_list_get(com->new_proto_list, 0);
523
0
      gf_list_rem(com->new_proto_list, 0);
524
0
      if (gf_list_del_item(graph->unregistered_protos, p) < 0)
525
0
        continue;
526
0
      gf_list_add(graph->protos, p);
527
0
    }
528
0
    return GF_OK;
529
0
  case GF_SG_PROTO_DELETE: {
530
0
    u32 i, j;
531
0
    for (i = 0; i < com->del_proto_list_size; i++) {
532
0
      GF_Proto* p;
533
      /*only delete protos registered in the graph: unregistered ones are still owned by the
534
      command that declared them*/
535
0
      j = 0;
536
0
      while ((p = (GF_Proto*)gf_list_enum(graph->protos, &j))) {
537
0
        if (p->ID == com->del_proto_list[i]) {
538
0
          gf_sg_proto_del(p);
539
0
          break;
540
0
        }
541
0
      }
542
0
    }
543
0
    return GF_OK;
544
0
  }
545
0
  case GF_SG_PROTO_DELETE_ALL:
546
    /*destroy all proto*/
547
0
    while (gf_list_count(graph->protos)) {
548
0
      GF_Proto *p = (GF_Proto*)gf_list_get(graph->protos, 0);
549
0
      gf_list_rem(graph->protos, 0);
550
      /*this will unregister the proto from the graph, so don't delete the chain entry*/
551
0
      gf_sg_proto_del(p);
552
0
    }
553
    /*DO NOT TOUCH UNREGISTERED PROTOS*/
554
0
    return GF_OK;
555
0
  case GF_SG_XREPLACE:
556
0
  {
557
0
    s32 pos = -2;
558
0
    GF_Node *target = NULL;
559
0
    GF_ChildNodeItem *list, *cur;
560
0
    GF_FieldInfo value;
561
0
    inf = (GF_CommandField*)gf_list_get(com->command_fields, 0);
562
0
    if (!inf) return GF_SG_UNKNOWN_NODE;
563
564
0
    e = gf_node_get_field(com->node, inf->fieldIndex, &field);
565
0
    if (e) return e;
566
0
    target = com->node;
567
568
0
    if (!gf_sg_vrml_is_sf_field(field.fieldType)) {
569
0
      GF_FieldInfo idxField;
570
0
      if ((inf->pos != -2) || com->toNodeID) {
571
0
        if (com->toNodeID) {
572
0
          GF_Node *idxNode = gf_sg_find_node(graph, com->toNodeID);
573
0
          if (!idxNode) return GF_SG_UNKNOWN_NODE;
574
575
0
          if (gf_node_get_field(idxNode, com->toFieldIndex, &idxField) != GF_OK) return GF_OK;
576
0
          pos = 0;
577
0
          switch (idxField.fieldType) {
578
0
          case GF_SG_VRML_SFBOOL:
579
0
            if (*(SFBool*)idxField.far_ptr) pos = 1;
580
0
            break;
581
0
          case GF_SG_VRML_SFINT32:
582
0
            if (*(SFInt32*)idxField.far_ptr >=0) pos = *(SFInt32*)idxField.far_ptr;
583
0
            break;
584
0
          case GF_SG_VRML_SFFLOAT:
585
0
            if ( (*(SFFloat *)idxField.far_ptr) >=0) pos = (s32) floor( FIX2FLT(*(SFFloat*)idxField.far_ptr) );
586
0
            break;
587
0
          case GF_SG_VRML_SFTIME:
588
0
            if ( (*(SFTime *)idxField.far_ptr) >=0) pos = (s32) floor( (*(SFTime *)idxField.far_ptr) );
589
0
            break;
590
0
          }
591
0
        } else {
592
0
          pos = inf->pos;
593
0
        }
594
0
      }
595
0
    }
596
    /*override target node*/
597
0
    if ((field.fieldType==GF_SG_VRML_MFNODE) && (pos>=-1) && com->ChildNodeTag) {
598
0
      target = gf_node_list_get_child(*(GF_ChildNodeItem **)field.far_ptr, pos);
599
0
      if (!target) return GF_SG_UNKNOWN_NODE;
600
0
      if (gf_node_get_field(target, com->child_field, &field) != GF_OK) return GF_SG_UNKNOWN_NODE;
601
0
      pos=-2;
602
0
    }
603
604
0
    if (com->fromNodeID) {
605
0
      GF_Node *fromNode = gf_sg_find_node(graph, com->fromNodeID);
606
0
      if (!fromNode) return GF_SG_UNKNOWN_NODE;
607
0
      e = gf_node_get_field(fromNode, com->fromFieldIndex, &value);
608
0
      if (e) return e;
609
0
    } else {
610
0
      value.far_ptr = inf->field_ptr;
611
0
      value.fieldType = inf->fieldType;
612
0
    }
613
    /*indexed replacement*/
614
0
    if (pos>=-1) {
615
      /*a well-formed indexed replacement always carries a field payload;
616
        a missing payload (inf->field_ptr NULL) reaches value.far_ptr as
617
        NULL and would fault on the deref below*/
618
0
      if (!value.far_ptr) return GF_NON_COMPLIANT_BITSTREAM;
619
      /*if MFNode remove the child and set new node*/
620
0
      if (field.fieldType == GF_SG_VRML_MFNODE) {
621
0
        GF_Node *nn = *(GF_Node**)value.far_ptr;
622
0
        gf_node_replace_child(target, (GF_ChildNodeItem**) field.far_ptr, pos, nn);
623
0
        if (nn) gf_node_register(nn, NULL);
624
0
      }
625
      /*erase the field item*/
626
0
      else {
627
0
        u32 sftype;
628
0
        if (!field.far_ptr) return GF_NON_COMPLIANT_BITSTREAM;
629
0
        if ((pos < 0) || ((u32) pos >= ((GenMFField *) field.far_ptr)->count) ) {
630
0
          pos = ((GenMFField *)field.far_ptr)->count - 1;
631
          /*may happen with text and default value*/
632
0
          if (pos < 0) {
633
0
            pos = 0;
634
0
            gf_sg_vrml_mf_alloc(field.far_ptr, field.fieldType, 1);
635
0
          }
636
0
        }
637
0
        e = gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, & slot_ptr, pos);
638
0
        if (e) return e;
639
0
        sftype = gf_sg_vrml_get_sf_type(field.fieldType);
640
0
        gf_sg_vrml_field_copy(slot_ptr, value.far_ptr, sftype);
641
        /*note we don't add time offset, since there's no MFTime*/
642
0
      }
643
0
    } else {
644
0
      GF_ChildNodeItem *prev;
645
0
      switch (field.fieldType) {
646
0
      case GF_SG_VRML_SFNODE:
647
0
      {
648
0
        node = *((GF_Node **) field.far_ptr);
649
0
        e = gf_node_unregister(node, target);
650
0
        *((GF_Node **) field.far_ptr) = *((GF_Node **) value.far_ptr) ;
651
0
        if (!e) gf_node_register(*(GF_Node **) value.far_ptr, target);
652
0
        break;
653
0
      }
654
0
      case GF_SG_VRML_MFNODE:
655
0
        if (!value.far_ptr) break;
656
0
        gf_node_unregister_children(target, * ((GF_ChildNodeItem **) field.far_ptr));
657
0
        * ((GF_ChildNodeItem **) field.far_ptr) = NULL;
658
0
        list = * ((GF_ChildNodeItem **) value.far_ptr);
659
0
        prev=NULL;
660
0
        while (list) {
661
0
          cur = gf_malloc(sizeof(GF_ChildNodeItem));
662
0
          cur->next = NULL;
663
0
          cur->node = list->node;
664
0
          if (prev) {
665
0
            prev->next = cur;
666
0
          } else {
667
0
            * ((GF_ChildNodeItem **) field.far_ptr) = cur;
668
0
          }
669
0
          gf_node_register(list->node, target);
670
0
          prev = cur;
671
0
          list = list->next;
672
0
        }
673
0
        break;
674
675
0
      default:
676
0
        if (!gf_sg_vrml_is_sf_field(field.fieldType)) {
677
0
          e = gf_sg_vrml_mf_reset(field.far_ptr, field.fieldType);
678
0
        }
679
0
        if (e) return e;
680
0
        gf_sg_vrml_field_clone(field.far_ptr, value.far_ptr, field.fieldType, graph);
681
682
0
        if ((field.fieldType==GF_SG_VRML_SFTIME) && !strstr(field.name, "media"))
683
0
          *(SFTime *)field.far_ptr = *(SFTime *)field.far_ptr + time_offset;
684
0
        break;
685
0
      }
686
0
    }
687
0
    SG_CheckFieldChange(target, &field);
688
0
  }
689
0
  return GF_OK;
690
  /*only used by BIFS*/
691
0
  case GF_SG_GLOBAL_QUANTIZER:
692
0
    return GF_OK;
693
694
0
#endif /*GPAC_DISABLE_VRML*/
695
696
697
0
#ifndef GPAC_DISABLE_SVG
698
  /*laser commands*/
699
0
  case GF_SG_LSR_NEW_SCENE:
700
    /*DO NOT TOUCH node registry*/
701
702
    /*assign new root (no need to register/unregister)*/
703
0
    graph->RootNode = com->node;
704
0
    gf_sg_command_set_node(com, NULL);
705
0
    break;
706
0
  case GF_SG_LSR_DELETE:
707
0
    if (!com->node) return GF_NON_COMPLIANT_BITSTREAM;
708
0
    if (!gf_list_count(com->command_fields)) {
709
0
      gf_node_deactivate(com->node);
710
0
      gf_node_replace(com->node, NULL, 0);
711
0
      return GF_OK;
712
0
    }
713
0
    inf = (GF_CommandField*)gf_list_get(com->command_fields, 0);
714
0
    node = gf_node_list_get_child(((SVG_Element *)com->node)->children, inf->pos);
715
0
    if (node) {
716
0
      gf_node_deactivate(node);
717
0
      e = gf_node_replace_child(com->node, &((SVG_Element *)com->node)->children, inf->pos, NULL);
718
0
    }
719
0
    break;
720
0
  case GF_SG_LSR_INSERT:
721
0
    inf = (GF_CommandField*)gf_list_get(com->command_fields, 0);
722
0
    if (!com->node || !inf) return GF_NON_COMPLIANT_BITSTREAM;
723
0
    if (inf->new_node) {
724
0
      if (inf->pos<0)
725
0
        gf_node_list_add_child(& ((SVG_Element *)com->node)->children, inf->new_node);
726
0
      else
727
0
        gf_node_list_insert_child(& ((SVG_Element *)com->node)->children, inf->new_node, inf->pos);
728
729
0
      gf_node_register(inf->new_node, com->node);
730
0
      gf_node_activate(inf->new_node);
731
0
      gf_node_changed(com->node, NULL);
732
0
    } else {
733
      /*NOT SUPPORTED*/
734
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[LASeR] VALUE INSERTION NOT SUPPORTED\n"));
735
0
    }
736
0
    break;
737
0
  case GF_SG_LSR_ADD:
738
0
  case GF_SG_LSR_REPLACE:
739
0
    inf = (GF_CommandField*)gf_list_get(com->command_fields, 0);
740
0
    if (!com->node || !inf) return GF_NON_COMPLIANT_BITSTREAM;
741
0
    if (inf->new_node) {
742
0
      if (inf->pos<0) {
743
        /*if fieldIndex (eg attributeName) is set, this is children replacement*/
744
0
        if (inf->fieldIndex>0) {
745
0
          GF_ChildNodeItem *old_children = ((SVG_Element *)com->node)->children;
746
0
          ((SVG_Element*)com->node)->children = NULL;
747
0
          gf_node_unregister_children_deactivate(com->node, old_children);
748
0
          gf_node_list_add_child(&((SVG_Element*)com->node)->children, inf->new_node);
749
0
          gf_node_register(inf->new_node, com->node);
750
0
          gf_node_activate(inf->new_node);
751
0
        } else {
752
0
          e = gf_node_replace(com->node, inf->new_node, 0);
753
0
          gf_node_activate(inf->new_node);
754
0
        }
755
0
      } else {
756
0
        node = gf_node_list_get_child( ((SVG_Element *)com->node)->children, inf->pos);
757
0
        if (node) gf_node_deactivate(node);
758
0
        gf_node_replace_child(com->node, & ((SVG_Element *)com->node)->children, inf->pos, inf->new_node);
759
0
        gf_node_register(inf->new_node, com->node);
760
0
        gf_node_activate(inf->new_node);
761
0
      }
762
      /*signal node modif*/
763
0
      gf_node_changed(com->node, NULL);
764
0
      return e;
765
0
    } else if (inf->node_list) {
766
0
      GF_ChildNodeItem *child, *cur, *prev;
767
0
      GF_ChildNodeItem* old_children = ((SVG_Element*)com->node)->children;
768
0
      ((SVG_Element*)com->node)->children = NULL;
769
0
      gf_node_unregister_children_deactivate(com->node, old_children);
770
771
0
      prev = NULL;
772
0
      child = inf->node_list;
773
0
      while (child) {
774
0
        cur = (GF_ChildNodeItem*)gf_malloc(sizeof(GF_ChildNodeItem));
775
0
        cur->next = NULL;
776
0
        cur->node = child->node;
777
0
        gf_node_register(child->node, com->node);
778
0
        gf_node_activate(child->node);
779
0
        if (prev) prev->next = cur;
780
0
        else ((SVG_Element *)com->node)->children = cur;
781
0
        prev = cur;
782
0
        child = child->next;
783
0
      }
784
      /*signal node modif*/
785
0
      gf_node_changed(com->node, NULL);
786
0
      return GF_OK;
787
0
    }
788
    /*attribute modif*/
789
0
    else if (inf->field_ptr) {
790
0
      GF_FieldInfo a, b;
791
0
      memset(&a, 0, sizeof(GF_FieldInfo));
792
0
      memset(&b, 0, sizeof(GF_FieldInfo));
793
0
      if (inf->fieldIndex==(u32) -2) {
794
0
        GF_Point2D scale, translate;
795
0
        Fixed rotate;
796
0
        GF_Matrix2D *dest;
797
0
        gf_node_get_field_by_name(com->node, "transform", &a);
798
0
        dest = (GF_Matrix2D*)a.far_ptr;
799
800
0
        if (com->tag==GF_SG_LSR_REPLACE) {
801
0
          if (gf_mx2d_decompose(dest, &scale, &rotate, &translate)) {
802
0
            gf_mx2d_init(*dest);
803
0
            if (inf->fieldType==SVG_TRANSFORM_SCALE) scale = *(GF_Point2D *)inf->field_ptr;
804
0
            else if (inf->fieldType==SVG_TRANSFORM_TRANSLATE) translate = *(GF_Point2D *)inf->field_ptr;
805
0
            else if (inf->fieldType==SVG_TRANSFORM_ROTATE) rotate = ((SVG_Point_Angle*)inf->field_ptr)->angle;
806
807
0
            gf_mx2d_add_scale(dest, scale.x, scale.y);
808
0
            gf_mx2d_add_rotation(dest, 0, 0, rotate);
809
0
            gf_mx2d_add_translation(dest, translate.x, translate.y);
810
0
          }
811
0
        } else {
812
0
          GF_Point2D *pt = (GF_Point2D *)inf->field_ptr;
813
0
          if (inf->fieldType==SVG_TRANSFORM_SCALE) gf_mx2d_add_scale(dest, pt->x, pt->y);
814
0
          else if (inf->fieldType==SVG_TRANSFORM_TRANSLATE) gf_mx2d_add_translation(dest, pt->x, pt->y);
815
0
          else if (inf->fieldType == SVG_TRANSFORM_ROTATE) gf_mx2d_add_rotation(dest, 0, 0, ((SVG_Point_Angle*)inf->field_ptr)->angle);
816
0
        }
817
        /*signal node modif*/
818
0
        gf_node_changed(com->node, &a);
819
0
      } else {
820
0
        if ((inf->fieldIndex==(u32) -1) && (inf->fieldType==DOM_String_datatype)) {
821
0
          char *str = *(SVG_String*)inf->field_ptr;
822
823
0
          if (com->tag == GF_SG_LSR_REPLACE) {
824
0
            GF_DOMText *t = ((SVG_Element*)com->node)->children ? (GF_DOMText*) ((SVG_Element*)com->node)->children->node :NULL;
825
0
            if (t && (t->sgprivate->tag==TAG_DOMText)) {
826
0
              if (t->textContent) gf_free(t->textContent);
827
0
              t->textContent = NULL;
828
0
              if (str) t->textContent = gf_strdup(str);
829
0
            }
830
0
          } else {
831
0
            if (str) gf_dom_add_text_node(com->node, gf_strdup(str));
832
0
          }
833
          /*signal node modif*/
834
0
          gf_node_changed(com->node, NULL);
835
0
        }
836
0
        else if ((inf->fieldIndex==TAG_LSR_ATT_scale)
837
0
                 || (inf->fieldIndex==TAG_LSR_ATT_translation)
838
0
                 || (inf->fieldIndex==TAG_LSR_ATT_rotation)
839
0
                ) {
840
0
          SVG_Transform *mx;
841
0
          gf_node_get_attribute_by_tag(com->node, TAG_SVG_ATT_transform, 1, 0, &a);
842
0
          mx = a.far_ptr;
843
0
          if (com->tag == GF_SG_LSR_REPLACE) {
844
0
            GF_Point2D scale, translate;
845
0
            SVG_Point_Angle rotate;
846
0
            if (gf_mx2d_decompose(&mx->mat, &scale, &rotate.angle, &translate)) {
847
0
              gf_mx2d_init(mx->mat);
848
0
              if (inf->fieldIndex==TAG_LSR_ATT_scale) scale = *(GF_Point2D *)inf->field_ptr;
849
0
              else if (inf->fieldIndex==TAG_LSR_ATT_translation) translate = *(GF_Point2D *)inf->field_ptr;
850
0
              else if (inf->fieldIndex==TAG_LSR_ATT_rotation) rotate = *(SVG_Point_Angle*)inf->field_ptr;
851
852
0
              gf_mx2d_add_scale(&mx->mat, scale.x, scale.y);
853
0
              gf_mx2d_add_rotation(&mx->mat, 0, 0, rotate.angle);
854
0
              gf_mx2d_add_translation(&mx->mat, translate.x, translate.y);
855
0
            }
856
0
          } else {
857
0
            if (inf->fieldIndex==TAG_LSR_ATT_scale) gf_mx2d_add_scale(&mx->mat, ((GF_Point2D*)inf->field_ptr)->x, ((GF_Point2D*)inf->field_ptr)->y);
858
0
            if (inf->fieldIndex==TAG_LSR_ATT_translation) gf_mx2d_add_translation(&mx->mat, ((GF_Point2D*)inf->field_ptr)->x, ((GF_Point2D*)inf->field_ptr)->y);
859
0
            if (inf->fieldIndex==TAG_LSR_ATT_rotation) gf_mx2d_add_rotation(&mx->mat, 0, 0, ((SVG_Point_Angle*)inf->field_ptr)->angle);
860
0
          }
861
          /*signal node modif*/
862
0
          gf_node_changed(com->node, &a);
863
0
        }
864
0
        else if (gf_node_get_attribute_by_tag(com->node, inf->fieldIndex, 1, 0, &a) == GF_OK) {
865
0
          b = a;
866
0
          b.far_ptr = inf->field_ptr;
867
0
          if (com->tag == GF_SG_LSR_REPLACE) {
868
0
            gf_svg_attributes_copy(&a, &b, 0);
869
0
          } else {
870
0
            gf_svg_attributes_add(&a, &b, &a, 0);
871
0
          }
872
0
          if (a.fieldType==XMLRI_datatype) {
873
0
            gf_node_dirty_set(com->node, GF_SG_SVG_XLINK_HREF_DIRTY, 0);
874
0
          }
875
          /*signal node modif*/
876
0
          gf_node_changed(com->node, &a);
877
0
        }
878
0
      }
879
0
    } else if (com->fromNodeID) {
880
0
      GF_FieldInfo a, b;
881
0
      memset(&a, 0, sizeof(GF_FieldInfo));
882
0
      memset(&b, 0, sizeof(GF_FieldInfo));
883
0
      GF_Node *fromNode = gf_sg_find_node(graph, com->fromNodeID);
884
0
      if (!fromNode) return GF_NON_COMPLIANT_BITSTREAM;
885
0
      if (gf_node_get_field(fromNode, com->fromFieldIndex, &b) != GF_OK) return GF_NON_COMPLIANT_BITSTREAM;
886
887
0
      if ((inf->fieldIndex==(u32) -1) && (inf->fieldType==DOM_String_datatype)) {
888
0
        char *str = inf->field_ptr ? *(SVG_String*)inf->field_ptr : NULL;
889
890
0
        if (com->tag == GF_SG_LSR_REPLACE) {
891
0
          GF_DOMText *t = ((SVG_Element*)com->node)->children ? (GF_DOMText*) ((SVG_Element*)com->node)->children->node :NULL;
892
0
          if (t && (t->sgprivate->tag==TAG_DOMText)) {
893
0
            if (t->textContent) gf_free(t->textContent);
894
0
            t->textContent = NULL;
895
0
            if (str) t->textContent = gf_strdup(str);
896
0
          }
897
0
        } else {
898
0
          if (str) gf_dom_add_text_node(com->node, gf_strdup(str));
899
0
        }
900
0
      } else {
901
0
        gf_node_get_field(com->node, inf->fieldIndex, &a);
902
0
        if (com->tag == GF_SG_LSR_REPLACE) {
903
0
          e = gf_svg_attributes_copy(&a, &b, 0);
904
0
        } else {
905
0
          e = gf_svg_attributes_add(&a, &b, &a, 0);
906
0
        }
907
0
      }
908
0
      gf_node_changed(com->node, &a);
909
0
      return e;
910
0
    } else {
911
0
      return GF_NON_COMPLIANT_BITSTREAM;
912
0
    }
913
0
    break;
914
0
  case GF_SG_LSR_ACTIVATE:
915
0
    gf_node_activate(com->node);
916
0
    break;
917
0
  case GF_SG_LSR_DEACTIVATE:
918
0
    gf_node_deactivate(com->node);
919
0
    gf_node_changed(com->node, NULL);
920
0
    break;
921
0
  case GF_SG_LSR_SEND_EVENT:
922
0
  {
923
0
    GF_DOM_Event evt;
924
0
    memset(&evt, 0, sizeof(GF_DOM_Event));
925
0
    evt.type = com->send_event_name;
926
0
    evt.detail = com->send_event_integer;
927
0
    evt.clientX = com->send_event_x;
928
0
    evt.clientY = com->send_event_y;
929
0
    gf_dom_event_fire(com->node, &evt);
930
0
  }
931
0
  break;
932
0
#endif
933
934
0
  default:
935
0
    return GF_NOT_SUPPORTED;
936
0
  }
937
0
  if (e) return e;
938
939
0
#ifndef GPAC_DISABLE_VRML
940
0
  if (com->scripts_to_load) {
941
0
    while (gf_list_count(com->scripts_to_load)) {
942
0
      GF_Node *script = (GF_Node *)gf_list_get(com->scripts_to_load, 0);
943
0
      gf_list_rem(com->scripts_to_load, 0);
944
0
      gf_sg_script_load(script);
945
0
    }
946
0
    gf_list_del(com->scripts_to_load);
947
0
    com->scripts_to_load = NULL;
948
0
  }
949
0
#endif
950
951
0
  return GF_OK;
952
0
}
953
954
GF_EXPORT
955
GF_CommandField *gf_sg_command_field_new(GF_Command *com)
956
0
{
957
0
  GF_CommandField *ptr;
958
0
  GF_SAFEALLOC(ptr, GF_CommandField);
959
0
  if (ptr)
960
0
    gf_list_add(com->command_fields, ptr);
961
0
  return ptr;
962
0
}
963
964
void gf_sg_command_set_node(GF_Command *com, GF_Node *node)
965
0
{
966
  /* remove the back-reference from the old node's tracking list */
967
0
  if (com->node && com->node->sgprivate && com->node->sgprivate->referencing_commands)
968
0
    gf_list_del_item(com->node->sgprivate->referencing_commands, &com->node);
969
970
0
  com->node = node;
971
972
0
  if (node) {
973
0
    if (!node->sgprivate->referencing_commands)
974
0
      node->sgprivate->referencing_commands = gf_list_new();
975
0
    gf_list_add(node->sgprivate->referencing_commands, &com->node);
976
0
  }
977
0
}
978
979
980
GF_EXPORT
981
GF_Err gf_sg_command_apply_list(GF_SceneGraph *graph, GF_List *comList, Double time_offset)
982
0
{
983
0
  GF_Err e;
984
0
  GF_Command *com;
985
0
  u32 i=0;
986
0
  while ((com = (GF_Command *)gf_list_enum(comList, &i))) {
987
0
    e = gf_sg_command_apply(graph, com, time_offset);
988
0
    if (e) return e;
989
0
  }
990
0
  return GF_OK;
991
0
}
992
993
#ifndef GPAC_DISABLE_VRML
994
GF_Command *gf_sg_vrml_command_clone(GF_Command *com, GF_SceneGraph *inGraph, Bool force_clone)
995
0
{
996
0
  u32 i, count;
997
0
  GF_Command *dest;
998
999
  /*FIXME - to do*/
1000
0
  if (gf_list_count(com->new_proto_list)) return NULL;
1001
0
  dest = gf_sg_command_new(inGraph, com->tag);
1002
1003
0
  if (com->in_scene!=inGraph) force_clone = 1;
1004
1005
  /*node the command applies to - may be NULL*/
1006
0
  if (force_clone) {
1007
0
    dest->node = gf_node_clone(inGraph, com->node, NULL, "", 0);
1008
0
  } else {
1009
0
    dest->node = com->node;
1010
0
    gf_node_register(dest->node, NULL);
1011
0
  }
1012
  /*route insert, replace and delete*/
1013
0
  dest->RouteID = com->RouteID;
1014
0
  if (com->def_name) dest->def_name = gf_strdup(com->def_name);
1015
  //this is an union
1016
  //if (com->send_event_string) dest->send_event_string = gf_strdup(com->send_event_string);
1017
1018
0
  dest->fromNodeID = com->fromNodeID;
1019
0
  dest->fromFieldIndex = com->fromFieldIndex;
1020
0
  dest->toNodeID = com->toNodeID;
1021
0
  dest->toFieldIndex = com->toFieldIndex;
1022
0
  dest->send_event_integer = com->send_event_integer;
1023
0
  dest->send_event_x = com->send_event_x;
1024
0
  dest->send_event_y = com->send_event_y;
1025
1026
0
  dest->del_proto_list_size = com->del_proto_list_size;
1027
0
  if (com->del_proto_list_size) {
1028
0
    dest->del_proto_list = (u32*)gf_malloc(sizeof(u32) * com->del_proto_list_size);
1029
0
    memcpy(dest->del_proto_list, com->del_proto_list, sizeof(u32) * com->del_proto_list_size);
1030
0
  }
1031
0
  count = gf_list_count(com->command_fields);
1032
0
  for (i=0; i<count; i++) {
1033
0
    GF_CommandField *fo = (GF_CommandField *)gf_list_get(com->command_fields, i);
1034
0
    GF_CommandField *fd = (GF_CommandField *)gf_sg_command_field_new(dest);
1035
1036
0
    fd->fieldIndex = fo->fieldIndex;
1037
0
    fd->fieldType = fo->fieldType;
1038
0
    fd->pos = fo->pos;
1039
1040
    /*FIXME - this can also be LASeR commands, not supported for now*/
1041
0
    if (fo->field_ptr) {
1042
0
      fd->field_ptr = gf_sg_vrml_field_pointer_new(fd->fieldType);
1043
0
      gf_sg_vrml_field_clone(fd->field_ptr, fo->field_ptr, fo->fieldType, dest->in_scene);
1044
0
    }
1045
1046
0
    if (fo->new_node) {
1047
0
      if (force_clone) {
1048
0
        fd->new_node = gf_node_clone(inGraph, fo->new_node, dest->node, "", 0);
1049
0
      } else {
1050
0
        fd->new_node = fo->new_node;
1051
0
        gf_node_register(fd->new_node, NULL);
1052
0
      }
1053
0
      fd->field_ptr = &fd->new_node;
1054
0
    }
1055
0
    if (fo->node_list) {
1056
0
      GF_ChildNodeItem *child, *cur, *prev;
1057
0
      prev = NULL;
1058
0
      child = fo->node_list;
1059
0
      while (child) {
1060
0
        cur = (GF_ChildNodeItem*) gf_malloc(sizeof(GF_ChildNodeItem));
1061
0
        if (force_clone) {
1062
0
          cur->node = gf_node_clone(inGraph, child->node, dest->node, "", 0);
1063
0
        } else {
1064
0
          cur->node = child->node;
1065
0
          gf_node_register(cur->node, NULL);
1066
0
        }
1067
0
        cur->next = NULL;
1068
0
        if (prev) prev->next = cur;
1069
0
        else fd->node_list = cur;
1070
0
        prev = cur;
1071
0
        child = child->next;
1072
0
      }
1073
0
      fd->field_ptr = &fd->node_list;
1074
0
    }
1075
0
  }
1076
0
  return dest;
1077
0
}
1078
1079
#endif