Home | History | Annotate | Download | only in skin
      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*
     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
     41 skin_background_done( SkinBackground*  background )
     42 {
     43     if (background->image)
     44         skin_image_unref(&background->image);
     45 }
     46 
     47 static int
     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
     81 skin_display_done( SkinDisplay*  display )
     82 {
     83     qframebuffer_done( display->qfbuff );
     84 }
     85 
     86 static int
     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
    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
    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*
    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
    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*
    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*
    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*
    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
    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*
    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
    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
    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*
    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
    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
    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*
    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
    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