• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (C) 2007-2008 The Android Open Source Project
2 **
3 ** This software is licensed under the terms of the GNU General Public
4 ** License version 2, as published by the Free Software Foundation, and
5 ** may be copied, distributed, and modified under those terms.
6 **
7 ** This program is distributed in the hope that it will be useful,
8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 ** GNU General Public License for more details.
11 */
12 #include "android/skin/file.h"
13 #include "android/utils/path.h"
14 #include "android/charmap.h"
15 #include "android/utils/bufprint.h"
16 #include "android/utils/system.h"
17 #include "android/utils/debug.h"
18 
19 //#include "qemu-common.h"
20 
21 /** UTILITY ROUTINES
22  **/
23 static SkinImage*
skin_image_find_in(const char * dirname,const char * filename)24 skin_image_find_in( const char*  dirname, const char*  filename )
25 {
26     char   buffer[1024];
27     char*  p   = buffer;
28     char*  end = p + sizeof(buffer);
29 
30     p = bufprint( p, end, "%s" PATH_SEP "%s", dirname, filename );
31     if (p >= end)
32         return SKIN_IMAGE_NONE;
33 
34     return skin_image_find_simple(buffer);
35 }
36 
37 /** SKIN BACKGROUND
38  **/
39 
40 static void
skin_background_done(SkinBackground * background)41 skin_background_done( SkinBackground*  background )
42 {
43     if (background->image)
44         skin_image_unref(&background->image);
45 }
46 
47 static int
skin_background_init_from(SkinBackground * background,AConfig * node,const char * basepath)48 skin_background_init_from( SkinBackground*  background,
49                            AConfig*         node,
50                            const char*      basepath )
51 {
52     const char* img = aconfig_str(node, "image", NULL);
53     int         x   = aconfig_int(node, "x", 0);
54     int         y   = aconfig_int(node, "y", 0);
55 
56     background->valid = 0;
57 
58     if (img == NULL)   /* no background */
59         return -1;
60 
61     background->image = skin_image_find_in( basepath, img );
62     if (background->image == SKIN_IMAGE_NONE) {
63         background->image = NULL;
64         return -1;
65     }
66 
67     background->rect.pos.x  = x;
68     background->rect.pos.y  = y;
69     background->rect.size.w = skin_image_w( background->image );
70     background->rect.size.h = skin_image_h( background->image );
71 
72     background->valid = 1;
73 
74     return 0;
75 }
76 
77 /** SKIN DISPLAY
78  **/
79 
80 static void
skin_display_done(SkinDisplay * display)81 skin_display_done( SkinDisplay*  display )
82 {
83     qframebuffer_done( display->qfbuff );
84 }
85 
86 static int
skin_display_init_from(SkinDisplay * display,AConfig * node)87 skin_display_init_from( SkinDisplay*  display, AConfig*  node )
88 {
89     display->rect.pos.x  = aconfig_int(node, "x", 0);
90     display->rect.pos.y  = aconfig_int(node, "y", 0);
91     display->rect.size.w = aconfig_int(node, "width", 0);
92     display->rect.size.h = aconfig_int(node, "height", 0);
93     display->rotation    = aconfig_unsigned(node, "rotation", SKIN_ROTATION_0);
94     display->bpp         = aconfig_int(node, "bpp", 16);
95 
96     display->valid = ( display->rect.size.w > 0 && display->rect.size.h > 0 );
97 
98     if (display->valid) {
99         SkinRect  r;
100         skin_rect_rotate( &r, &display->rect, -display->rotation );
101         qframebuffer_init( display->qfbuff,
102                            r.size.w,
103                            r.size.h,
104                            0,
105                            display->bpp == 32 ? QFRAME_BUFFER_RGBX_8888
106                                               : QFRAME_BUFFER_RGB565 );
107 
108         qframebuffer_fifo_add( display->qfbuff );
109     }
110     return display->valid ? 0 : -1;
111 }
112 
113 /** SKIN BUTTON
114  **/
115 
116 typedef struct
117 {
118     const char*     name;
119     AndroidKeyCode  code;
120 } KeyInfo;
121 
122 static KeyInfo  _keyinfo_table[] = {
123     { "dpad-up",      kKeyCodeDpadUp },
124     { "dpad-down",    kKeyCodeDpadDown },
125     { "dpad-left",    kKeyCodeDpadLeft },
126     { "dpad-right",   kKeyCodeDpadRight },
127     { "dpad-center",  kKeyCodeDpadCenter },
128     { "soft-left",    kKeyCodeSoftLeft },
129     { "soft-right",   kKeyCodeSoftRight },
130     { "search",       kKeyCodeSearch },
131     { "camera",       kKeyCodeCamera },
132     { "volume-up",    kKeyCodeVolumeUp },
133     { "volume-down",  kKeyCodeVolumeDown },
134     { "power",        kKeyCodePower },
135     { "home",         kKeyCodeHome },
136     { "back",         kKeyCodeBack },
137     { "del",          kKeyCodeDel },
138     { "0",            kKeyCode0 },
139     { "1",            kKeyCode1 },
140     { "2",            kKeyCode2 },
141     { "3",            kKeyCode3 },
142     { "4",            kKeyCode4 },
143     { "5",            kKeyCode5 },
144     { "6",            kKeyCode6 },
145     { "7",            kKeyCode7 },
146     { "8",            kKeyCode8 },
147     { "9",            kKeyCode9 },
148     { "star",         kKeyCodeStar },
149     { "pound",        kKeyCodePound },
150     { "phone-dial",   kKeyCodeCall },
151     { "phone-hangup", kKeyCodeEndCall },
152     { "q",            kKeyCodeQ },
153     { "w",            kKeyCodeW },
154     { "e",            kKeyCodeE },
155     { "r",            kKeyCodeR },
156     { "t",            kKeyCodeT },
157     { "y",            kKeyCodeY },
158     { "u",            kKeyCodeU },
159     { "i",            kKeyCodeI },
160     { "o",            kKeyCodeO },
161     { "p",            kKeyCodeP },
162     { "a",            kKeyCodeA },
163     { "s",            kKeyCodeS },
164     { "d",            kKeyCodeD },
165     { "f",            kKeyCodeF },
166     { "g",            kKeyCodeG },
167     { "h",            kKeyCodeH },
168     { "j",            kKeyCodeJ },
169     { "k",            kKeyCodeK },
170     { "l",            kKeyCodeL },
171     { "DEL",          kKeyCodeDel },
172     { "z",            kKeyCodeZ },
173     { "x",            kKeyCodeX },
174     { "c",            kKeyCodeC },
175     { "v",            kKeyCodeV },
176     { "b",            kKeyCodeB },
177     { "n",            kKeyCodeN },
178     { "m",            kKeyCodeM },
179     { "COMMA",        kKeyCodeComma },
180     { "PERIOD",       kKeyCodePeriod },
181     { "ENTER",        kKeyCodeNewline },
182     { "AT",           kKeyCodeAt },
183     { "SPACE",        kKeyCodeSpace },
184     { "SLASH",        kKeyCodeSlash },
185     { "CAP",          kKeyCodeCapLeft },
186     { "SYM",          kKeyCodeSym },
187     { "ALT",          kKeyCodeAltLeft },
188     { "ALT2",         kKeyCodeAltRight },
189     { "CAP2",         kKeyCodeCapRight },
190     { "tv",           kKeyCodeTV },
191     { "epg",          kKeyCodeEPG },
192     { "dvr",          kKeyCodeDVR },
193     { "prev",         kKeyCodePrevious },
194     { "next",         kKeyCodeNext },
195     { "play",         kKeyCodePlay },
196     { "pause",        kKeyCodePause },
197     { "stop",         kKeyCodeStop },
198     { "rev",          kKeyCodeRewind },
199     { "ffwd",         kKeyCodeFastForward },
200     { "bookmarks",    kKeyCodeBookmarks },
201     { "window",       kKeyCodeCycleWindows },
202     { "channel-up",   kKeyCodeChannelUp },
203     { "channel-down", kKeyCodeChannelDown },
204     { 0, 0 },
205 };
206 
207 static unsigned
keyinfo_lookup_code(const char * name)208 keyinfo_lookup_code(const char *name)
209 {
210     KeyInfo *ki = _keyinfo_table;
211     while(ki->name) {
212         if(!strcmp(name, ki->name))
213             return ki->code;
214         ki++;
215     }
216     return 0;
217 }
218 
219 
220 static void
skin_button_free(SkinButton * button)221 skin_button_free( SkinButton*  button )
222 {
223     if (button) {
224         skin_image_unref( &button->image );
225         AFREE(button);
226     }
227 }
228 
229 static SkinButton*
skin_button_create_from(AConfig * node,const char * basepath)230 skin_button_create_from( AConfig*   node, const char*  basepath )
231 {
232     SkinButton*  button;
233     ANEW0(button);
234     if (button) {
235         const char*  img = aconfig_str(node, "image", NULL);
236         int          x   = aconfig_int(node, "x", 0);
237         int          y   = aconfig_int(node, "y", 0);
238 
239         button->name       = node->name;
240         button->rect.pos.x = x;
241         button->rect.pos.y = y;
242 
243         if (img != NULL)
244             button->image = skin_image_find_in( basepath, img );
245 
246         if (button->image == SKIN_IMAGE_NONE) {
247             skin_button_free(button);
248             return NULL;
249         }
250 
251         button->rect.size.w = skin_image_w( button->image );
252         button->rect.size.h = skin_image_h( button->image );
253 
254         button->keycode = keyinfo_lookup_code( button->name );
255         if (button->keycode == 0) {
256             dprint( "Warning: skin file button uses unknown key name '%s'", button->name );
257         }
258     }
259     return button;
260 }
261 
262 /** SKIN PART
263  **/
264 
265 static void
skin_part_free(SkinPart * part)266 skin_part_free( SkinPart*  part )
267 {
268     if (part) {
269         skin_background_done( part->background );
270         skin_display_done( part->display );
271 
272         SKIN_PART_LOOP_BUTTONS(part,button)
273             skin_button_free(button);
274         SKIN_PART_LOOP_END
275         part->buttons = NULL;
276         AFREE(part);
277     }
278 }
279 
280 static SkinLocation*
skin_location_create_from_v2(AConfig * node,SkinPart * parts)281 skin_location_create_from_v2( AConfig*  node, SkinPart*  parts )
282 {
283     const char*    partname = aconfig_str(node, "name", NULL);
284     int            x        = aconfig_int(node, "x", 0);
285     int            y        = aconfig_int(node, "y", 0);
286     SkinRotation   rot      = aconfig_int(node, "rotation", SKIN_ROTATION_0);
287     SkinPart*      part;
288     SkinLocation*  location;
289 
290     if (partname == NULL) {
291         dprint( "### WARNING: ignoring part location without 'name' element" );
292         return NULL;
293     }
294 
295     for (part = parts; part; part = part->next)
296         if (!strcmp(part->name, partname))
297             break;
298 
299     if (part == NULL) {
300         dprint( "### WARNING: ignoring part location with unknown name '%s'", partname );
301         return NULL;
302     }
303 
304     ANEW0(location);
305     location->part     = part;
306     location->anchor.x = x;
307     location->anchor.y = y;
308     location->rotation = rot;
309 
310     return location;
311 }
312 
313 static SkinPart*
skin_part_create_from_v1(AConfig * root,const char * basepath)314 skin_part_create_from_v1( AConfig*  root, const char*  basepath )
315 {
316     SkinPart*  part;
317     AConfig*  node;
318     SkinBox   box;
319 
320     ANEW0(part);
321     part->name = root->name;
322 
323     node = aconfig_find(root, "background");
324     if (node)
325         skin_background_init_from(part->background, node, basepath);
326 
327     node = aconfig_find(root, "display");
328     if (node)
329         skin_display_init_from(part->display, node);
330 
331     node = aconfig_find(root, "button");
332     if (node) {
333         for (node = node->first_child; node != NULL; node = node->next)
334         {
335             SkinButton*  button = skin_button_create_from(node, basepath);
336 
337             if (button != NULL) {
338                 button->next  = part->buttons;
339                 part->buttons = button;
340             }
341         }
342     }
343 
344     skin_box_minmax_init( &box );
345 
346     if (part->background->valid)
347         skin_box_minmax_update( &box, &part->background->rect );
348 
349     if (part->display->valid)
350         skin_box_minmax_update( &box, &part->display->rect );
351 
352     SKIN_PART_LOOP_BUTTONS(part, button)
353         skin_box_minmax_update( &box, &button->rect );
354     SKIN_PART_LOOP_END
355 
356     if ( !skin_box_minmax_to_rect( &box, &part->rect ) ) {
357         skin_part_free(part);
358         part = NULL;
359     }
360 
361     return part;
362 }
363 
364 static SkinPart*
skin_part_create_from_v2(AConfig * root,const char * basepath)365 skin_part_create_from_v2( AConfig*  root, const char*  basepath )
366 {
367     SkinPart*  part;
368     AConfig*  node;
369     SkinBox   box;
370 
371     ANEW0(part);
372     part->name = root->name;
373 
374     node = aconfig_find(root, "background");
375     if (node)
376         skin_background_init_from(part->background, node, basepath);
377 
378     node = aconfig_find(root, "display");
379     if (node)
380         skin_display_init_from(part->display, node);
381 
382     node = aconfig_find(root, "buttons");
383     if (node) {
384         for (node = node->first_child; node != NULL; node = node->next)
385         {
386             SkinButton*  button = skin_button_create_from(node, basepath);
387 
388             if (button != NULL) {
389                 button->next  = part->buttons;
390                 part->buttons = button;
391             }
392         }
393     }
394 
395     skin_box_minmax_init( &box );
396 
397     if (part->background->valid)
398         skin_box_minmax_update( &box, &part->background->rect );
399 
400     if (part->display->valid)
401         skin_box_minmax_update( &box, &part->display->rect );
402 
403     SKIN_PART_LOOP_BUTTONS(part, button)
404         skin_box_minmax_update( &box, &button->rect );
405     SKIN_PART_LOOP_END
406 
407     if ( !skin_box_minmax_to_rect( &box, &part->rect ) ) {
408         skin_part_free(part);
409         part = NULL;
410     }
411     return part;
412 }
413 
414 /** SKIN LAYOUT
415  **/
416 
417 static void
skin_layout_free(SkinLayout * layout)418 skin_layout_free( SkinLayout*  layout )
419 {
420     if (layout) {
421         SKIN_LAYOUT_LOOP_LOCS(layout,loc)
422             AFREE(loc);
423         SKIN_LAYOUT_LOOP_END
424         layout->locations = NULL;
425         if (layout->onion_image) {
426           skin_image_unref( &layout->onion_image );
427         }
428         AFREE(layout);
429     }
430 }
431 
432 SkinDisplay*
skin_layout_get_display(SkinLayout * layout)433 skin_layout_get_display( SkinLayout*  layout )
434 {
435     SKIN_LAYOUT_LOOP_LOCS(layout,loc)
436         SkinPart*  part = loc->part;
437         if (part->display->valid) {
438             return part->display;
439         }
440     SKIN_LAYOUT_LOOP_END
441     return NULL;
442 }
443 
444 SkinRotation
skin_layout_get_dpad_rotation(SkinLayout * layout)445 skin_layout_get_dpad_rotation( SkinLayout*  layout )
446 {
447     if (layout->has_dpad_rotation)
448         return layout->dpad_rotation;
449 
450     SKIN_LAYOUT_LOOP_LOCS(layout, loc)
451         SkinPart*  part = loc->part;
452         SKIN_PART_LOOP_BUTTONS(part,button)
453             if (button->keycode == kKeyCodeDpadUp)
454                 return loc->rotation;
455         SKIN_PART_LOOP_END
456     SKIN_LAYOUT_LOOP_END
457 
458     return SKIN_ROTATION_0;
459 }
460 
461 
462 static int
skin_layout_event_decode(const char * event,int * ptype,int * pcode,int * pvalue)463 skin_layout_event_decode( const char*  event, int  *ptype, int  *pcode, int *pvalue )
464 {
465     typedef struct {
466         const char*  name;
467         int          value;
468     } EventName;
469 
470     static const EventName  _event_names[] = {
471         { "EV_SW", 0x05 },
472         { NULL, 0 },
473     };
474 
475     const char*       x = strchr(event, ':');
476     const char*       y = NULL;
477     const EventName*  ev = _event_names;
478 
479     if (x != NULL)
480         y = strchr(x+1, ':');
481 
482     if (x == NULL || y == NULL) {
483         dprint( "### WARNING: invalid skin layout event format: '%s', should be '<TYPE>:<CODE>:<VALUE>'", event );
484         return -1;
485     }
486 
487     for ( ; ev->name != NULL; ev++ )
488         if (!memcmp( event, ev->name, x - event ) && ev->name[x-event] == 0)
489             break;
490 
491     if (!ev->name) {
492         dprint( "### WARNING: unrecognized skin layout event name: %.*s", x-event, event );
493         return -1;
494     }
495 
496     *ptype  = ev->value;
497     *pcode  = strtol(x+1, NULL, 0);
498     *pvalue = strtol(y+1, NULL, 0);
499     return 0;
500 }
501 
502 static SkinLayout*
skin_layout_create_from_v2(AConfig * root,SkinPart * parts,const char * basepath)503 skin_layout_create_from_v2( AConfig*  root, SkinPart*  parts, const char*  basepath )
504 {
505     SkinLayout*    layout;
506     int            width, height;
507     SkinLocation** ptail;
508     AConfig*       node;
509 
510     ANEW0(layout);
511 
512     width  = aconfig_int( root, "width", 400 );
513     height = aconfig_int( root, "height", 400 );
514 
515     node = aconfig_find( root, "event" );
516     if (node != NULL) {
517         skin_layout_event_decode( node->value,
518                                   &layout->event_type,
519                                   &layout->event_code,
520                                   &layout->event_value );
521     } else {
522         layout->event_type  = 0x05;  /* close keyboard by default */
523         layout->event_code  = 0;
524         layout->event_value = 1;
525     }
526 
527     layout->name  = root->name;
528     layout->color = aconfig_unsigned( root, "color", 0x808080 ) | 0xff000000;
529     ptail         = &layout->locations;
530 
531     node = aconfig_find( root, "dpad-rotation" );
532     if (node != NULL) {
533         layout->dpad_rotation     = aconfig_int( root, "dpad-rotation", 0 );
534         layout->has_dpad_rotation = 1;
535     }
536 
537     node = aconfig_find( root, "onion" );
538     if (node != NULL) {
539         const char* img = aconfig_str(node, "image", NULL);
540         layout->onion_image = skin_image_find_in( basepath, img );
541         if (layout->onion_image == SKIN_IMAGE_NONE) {
542             layout->onion_image = NULL;
543         }
544         // In layout file, alpha is specified in range 0-100. Convert to
545         // internal range 0-256 with default=128.
546         int alpha = aconfig_int( node, "alpha", 50 );
547         layout->onion_alpha = (256*alpha)/100;
548         layout->onion_rotation = aconfig_int( node, "rotation", 0 );
549     }
550 
551     for (node = root->first_child; node; node = node->next)
552     {
553         if (!memcmp(node->name, "part", 4)) {
554             SkinLocation*  location = skin_location_create_from_v2( node, parts );
555             if (location == NULL) {
556                 continue;
557             }
558             *ptail = location;
559             ptail  = &location->next;
560         }
561     }
562 
563     if (layout->locations == NULL)
564         goto Fail;
565 
566     layout->size.w = width;
567     layout->size.h = height;
568 
569     return layout;
570 
571 Fail:
572     skin_layout_free(layout);
573     return NULL;
574 }
575 
576 /** SKIN FILE
577  **/
578 
579 static int
skin_file_load_from_v1(SkinFile * file,AConfig * aconfig,const char * basepath)580 skin_file_load_from_v1( SkinFile*  file, AConfig*  aconfig, const char*  basepath )
581 {
582     SkinPart*      part;
583     SkinLayout*    layout;
584     SkinLayout**   ptail = &file->layouts;
585     SkinLocation*  location;
586     int            nn;
587 
588     file->parts = part = skin_part_create_from_v1( aconfig, basepath );
589     if (part == NULL)
590         return -1;
591 
592     for (nn = 0; nn < 2; nn++)
593     {
594         ANEW0(layout);
595 
596         layout->color = 0xff808080;
597 
598         ANEW0(location);
599 
600         layout->event_type  = 0x05;  /* close keyboard by default */
601         layout->event_code  = 0;
602         layout->event_value = 1;
603 
604         location->part     = part;
605         switch (nn) {
606             case 0:
607                 location->anchor.x = 0;
608                 location->anchor.y = 0;
609                 location->rotation = SKIN_ROTATION_0;
610                 layout->size       = part->rect.size;
611                 break;
612 
613 #if 0
614             case 1:
615                 location->anchor.x = part->rect.size.h;
616                 location->anchor.y = 0;
617                 location->rotation = SKIN_ROTATION_90;
618                 layout->size.w     = part->rect.size.h;
619                 layout->size.h     = part->rect.size.w;
620                 layout->event_value = 0;
621                 break;
622 
623             case 2:
624                 location->anchor.x = part->rect.size.w;
625                 location->anchor.y = part->rect.size.h;
626                 location->rotation = SKIN_ROTATION_180;
627                 layout->size       = part->rect.size;
628                 break;
629 #endif
630             default:
631                 location->anchor.x = 0;
632                 location->anchor.y = part->rect.size.w;
633                 location->rotation = SKIN_ROTATION_270;
634                 layout->size.w     = part->rect.size.h;
635                 layout->size.h     = part->rect.size.w;
636                 layout->event_value = 0;
637                 break;
638         }
639         layout->locations = location;
640 
641         *ptail = layout;
642         ptail  = &layout->next;
643     }
644     file->version = 1;
645     return 0;
646 }
647 
648 static int
skin_file_load_from_v2(SkinFile * file,AConfig * aconfig,const char * basepath)649 skin_file_load_from_v2( SkinFile*  file, AConfig*  aconfig, const char*  basepath )
650 {
651     AConfig*  node;
652 
653     /* first, load all parts */
654     node = aconfig_find(aconfig, "parts");
655     if (node == NULL)
656         return -1;
657     else
658     {
659         SkinPart**  ptail = &file->parts;
660         for (node = node->first_child; node != NULL; node = node->next)
661         {
662             SkinPart*  part = skin_part_create_from_v2( node, basepath );
663             if (part == NULL) {
664                 dprint( "## WARNING: can't load part '%s' from skin\n", node->name ? "<NULL>" : node->name );
665                 continue;
666             }
667             part->next = NULL;
668             *ptail     = part;
669             ptail      = &part->next;
670         }
671     }
672 
673     if (file->parts == NULL)
674         return -1;
675 
676     /* then load all layouts */
677     node = aconfig_find(aconfig, "layouts");
678     if (node == NULL)
679         return -1;
680     else
681     {
682         SkinLayout**  ptail = &file->layouts;
683         for (node = node->first_child; node != NULL; node = node->next)
684         {
685             SkinLayout*  layout = skin_layout_create_from_v2( node, file->parts, basepath );
686             if (layout == NULL) {
687                 dprint( "## WARNING: ignoring layout in skin file" );
688                 continue;
689             }
690             *ptail = layout;
691             layout->next = NULL;
692             ptail        = &layout->next;
693         }
694     }
695     if (file->layouts == NULL)
696         return -1;
697 
698     file->version = 2;
699     return 0;
700 }
701 
702 SkinFile*
skin_file_create_from_aconfig(AConfig * aconfig,const char * basepath)703 skin_file_create_from_aconfig( AConfig*   aconfig, const char*  basepath )
704 {
705     SkinFile*  file;
706 
707     ANEW0(file);
708 
709     if ( aconfig_find(aconfig, "parts") != NULL) {
710         if (skin_file_load_from_v2( file, aconfig, basepath ) < 0) {
711             goto BAD_FILE;
712         }
713         file->version = aconfig_int(aconfig, "version", 2);
714         /* The file version must be 1 or higher */
715         if (file->version <= 0) {
716             dprint( "## WARNING: invalid skin version: %d", file->version);
717             goto BAD_FILE;
718         }
719     }
720     else {
721         if (skin_file_load_from_v1( file, aconfig, basepath ) < 0) {
722             goto BAD_FILE;
723         }
724         file->version = 1;
725     }
726     return file;
727 
728 BAD_FILE:
729     skin_file_free( file );
730     return NULL;
731 }
732 
733 void
skin_file_free(SkinFile * file)734 skin_file_free( SkinFile*  file )
735 {
736     if (file) {
737         SKIN_FILE_LOOP_LAYOUTS(file,layout)
738             skin_layout_free(layout);
739         SKIN_FILE_LOOP_END_LAYOUTS
740         file->layouts = NULL;
741 
742         SKIN_FILE_LOOP_PARTS(file,part)
743             skin_part_free(part);
744         SKIN_FILE_LOOP_END_PARTS
745         file->parts = NULL;
746 
747         AFREE(file);
748     }
749 }
750