/** * @brief Parse a @a dlabel definition. * * Syntax: dlabel=x,y,width,align,fontfile,"text" * * @param in definition to be analyzed * * @return 0 (ok) or 1 (error) */ static int item_dlabel(char *in) { int x, y, w, a, id; char fnt[256]; char txt[256]; wItem *item; if (!window_item("dlabel")) return 1; if (in_window("video")) return 1; if (in_window("menu")) return 1; x = cutItemToInt(in, ',', 0); y = cutItemToInt(in, ',', 1); w = cutItemToInt(in, ',', 2); a = cutItemToInt(in, ',', 3); cutItem(in, fnt, ',', 4); cutItem(in, txt, ',', 5); cutItem(txt, txt, '"', 1); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] dlabel: \"%s\"\n", txt); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] pos: %d,%d\n", x, y); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] width: %d, align: %d\n", w, a); id = fntFindID(fnt); if (id < 0) { skin_error(MSGTR_SKIN_FONT_NonExistentFont, fnt); return 1; } mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] font: %s (#%d)\n", fnt, id); item = next_item(); if (!item) return 1; item->type = itDLabel; item->x = x; item->y = y; item->width = w; item->height = -1; item->fontid = id; item->align = a; item->label = strdup(txt); if (!item->label) { skin_error(MSGTR_SKIN_NotEnoughMemory); return 1; } return 0; }
/** * @brief Parse a @a decoration definition. * * Syntax: decoration=enable|disable * * @param in definition to be analyzed * * @return 0 (ok) or 1 (error) */ static int item_decoration(char *in) { if (!window_item("decoration")) return 1; if (in_window("video")) return 1; if (in_window("playbar")) return 1; if (in_window("menu")) return 1; strlower(in); if (strcmp(in, "enable") != 0 && strcmp(in, "disable") != 0) { skin_error(MSGTR_SKIN_UnknownParameter, in); return 1; } skin->mainDecoration = (strcmp(in, "enable") == 0); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] decoration: %s\n", in); return 0; }
/** * @brief Check whether a @a section definition has started. * * @param item name of the item to be put in a message in case of an error * * @return 1 (ok) or 0 (error) */ static int section_item(char *item) { if (!skin) { skin_error(MSGTR_SKIN_ERROR_SECTION, item); return 0; } return 1; }
/** * @brief Check whether a @a window definition has started. * * @param item name of the item to be put in a message in case of an error * * @return 1 (ok) or 0 (error) */ static int window_item(char *item) { if (!currWinName[0]) { skin_error(MSGTR_SKIN_ERROR_WINDOW, item); return 0; } return 1; }
/** * @brief Parse a @a section definition. * * Syntax: section=movieplayer * * @param in definition to be analyzed * * @return 0 (ok) or 1 (error) */ static int item_section(char *in) { if (skin) { skin_error(MSGTR_SKIN_ERROR_ITEM, "section"); return 1; } if (!strcmp(strlower(in), "movieplayer")) skin = &guiApp; else { skin_error(MSGTR_SKIN_UNKNOWN_NAME, in); return 1; } mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] section: %s\n", in); return 0; }
/** * @brief Check whether a specific @a window definition has started. * * @param name name of the window to be checked * * @return 0 (ok) or 1 (error) */ static int in_window(char *name) { if (strcmp(currWinName, name) == 0) { skin_error(MSGTR_SKIN_ERROR_ITEM, name); return 1; } return 0; }
char* scan_string(const char** document) { const char* cursor = *document; int length = 0; char* buffer = NULL; int i; while(*cursor != ARGLISTSEPARATESYM && *cursor != ARGLISTCLOSESYM && *cursor != '\0') { if(*cursor == COMMENTSYM) { skip_comment(&cursor); continue; } if(*cursor == TAGSYM) cursor++; if(*cursor == '\n') { skin_error(UNEXPECTED_NEWLINE, cursor); return NULL; } length++; cursor++; } /* Copying the string */ cursor = *document; buffer = skin_alloc_string(length); if (!buffer) return NULL; buffer[length] = '\0'; for(i = 0; i < length; i++) { if(*cursor == TAGSYM) cursor++; if(*cursor == COMMENTSYM) { skip_comment(&cursor); i--; continue; } buffer[i] = *cursor; cursor++; } *document = cursor; return buffer; }
/** * @brief Parse a @a window definition. * * Syntax: window=main|video|playbar|menu * * @param in definition to be analyzed * * @return 0 (ok) or 1 (error) */ static int item_window(char *in) { if (!section_item("window")) return 1; if (currWinName[0]) { skin_error(MSGTR_SKIN_ERROR_ITEM, "window"); return 1; } strlower(in); if (strcmp(in, "sub") == 0) strcpy(in, "video"); // legacy if (strcmp(in, "main") == 0) { currWin = &skin->main; currWinItemIdx = &skin->IndexOfMainItems; currWinItems = skin->mainItems; } else if (strcmp(in, "video") == 0) { currWin = &skin->video; currWinItemIdx = NULL; currWinItems = NULL; } else if (strcmp(in, "playbar") == 0) { currWin = &skin->playbar; currWinItemIdx = &skin->IndexOfPlaybarItems; currWinItems = skin->playbarItems; } else if (strcmp(in, "menu") == 0) { currWin = &skin->menu; currWinItemIdx = &skin->IndexOfMenuItems; currWinItems = skin->menuItems; } else { skin_error(MSGTR_SKIN_UNKNOWN_NAME, in); return 1; } av_strlcpy(currWinName, in, sizeof(currWinName)); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] window: %s\n", currWinName); return 0; }
/** * @brief Get next free item in current @a window. * * @return pointer to next free item (ok) or NULL (error) */ static wItem *next_item(void) { wItem *item = NULL; if (*currWinItemIdx < MAX_ITEMS - 1) { (*currWinItemIdx)++; item = &currWinItems[*currWinItemIdx]; } else skin_error(MSGTR_SKIN_TooManyItemsDeclared); return item; }
/** * @brief Parse a @a font definition. * * Syntax: font=fontfile * * @param in definition to be analyzed * * @return 0 (ok) or 1 (error) */ static int item_font(char *in) { char fnt[256]; if (!window_item("font")) return 1; if (in_window("video")) return 1; if (in_window("menu")) return 1; cutItem(in, fnt, ',', 0); // Note: This seems needless but isn't for compatibility // reasons with a meanwhile depreciated second parameter. switch (fntRead(path, fnt)) { case -1: skin_error(MSGTR_SKIN_NotEnoughMemory); return 1; case -2: skin_error(MSGTR_SKIN_FONT_TooManyFontsDeclared); return 1; case -3: skin_error(MSGTR_SKIN_FONT_FontFileNotFound); return 1; case -4: skin_error(MSGTR_SKIN_FONT_FontImageNotFound); return 1; } mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] font: %s (#%d)\n", fnt, fntFindID(fnt)); return 0; }
/** * @brief Read a skin @a image file. * * @param fname filename (with path) * @param img pointer suitable to store the image data * * @return return code of #bpRead() */ int skinImageRead(char *fname, guiImage *img) { int i = bpRead(fname, img); switch (i) { case -1: skin_error(MSGTR_SKIN_BITMAP_16bit, fname); break; case -2: skin_error(MSGTR_SKIN_BITMAP_FileNotFound, fname); break; case -5: skin_error(MSGTR_SKIN_BITMAP_PNGReadError, fname); break; case -8: skin_error(MSGTR_SKIN_BITMAP_ConversionError, fname); break; } return i; }
/** * @brief Parse a @a menu definition. * * Syntax: menu=x,y,width,height,message * * @param in definition to be analyzed * * @return 0 (ok) or 1 (error) */ static int item_menu(char *in) { int x, y, w, h, message; char msg[32]; wItem *item; if (!window_item("menu")) return 1; if (in_window("main")) return 1; if (in_window("video")) return 1; if (in_window("playbar")) return 1; x = cutItemToInt(in, ',', 0); y = cutItemToInt(in, ',', 1); w = cutItemToInt(in, ',', 2); h = cutItemToInt(in, ',', 3); cutItem(in, msg, ',', 4); message = appFindMessage(msg); if (message == -1) { skin_error(MSGTR_SKIN_UnknownMessage, msg); return 1; } item = next_item(); if (!item) return 1; item->type = itMenu; item->x = x; item->y = y; item->width = w; item->height = h; item->message = message; mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] item #%d: %d,%d %dx%d\n", *currWinItemIdx, x, y, w, h); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] message: %s (#%d)\n", msg, message); item->Bitmap.Image = NULL; return 0; }
/** * @brief Parse a @a potmeter definition. * * Syntax: potmeter=phases,numphases,default,x,y,width,height,message * * @param in definition to be analyzed * * @return 0 (ok) or 1 (error) */ static int item_potmeter(char *in) { unsigned char phfname[256]; unsigned char buf[512]; int ph, d, x, y, w, h, message; wItem *item; if (!window_item("potmeter")) return 1; if (in_window("video")) return 1; if (in_window("menu")) return 1; cutItem(in, phfname, ',', 0); ph = cutItemToInt(in, ',', 1); d = cutItemToInt(in, ',', 2); x = cutItemToInt(in, ',', 3); y = cutItemToInt(in, ',', 4); w = cutItemToInt(in, ',', 5); h = cutItemToInt(in, ',', 6); cutItem(in, buf, ',', 7); message = appFindMessage(buf); if (message == -1) { skin_error(MSGTR_SKIN_UnknownMessage, buf); return 1; } mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] potmeter image: %s %d,%d %dx%d\n", phfname, x, y, w, h); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] numphases: %d, default: %d%%\n", ph, d); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] message: %s (#%d)\n", buf, message); item = next_item(); if (!item) return 1; item->type = itPotmeter; item->x = x; item->y = y; item->width = w; item->height = h; item->numphases = ph; item->value = (float)d; item->message = message; item->Bitmap.Image = NULL; if (strcmp(phfname, "NULL") != 0) { av_strlcpy(buf, path, sizeof(buf)); av_strlcat(buf, phfname, sizeof(buf)); if (skinImageRead(buf, &item->Bitmap) != 0) return 1; mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] (bitmap: %lux%lu)\n", item->Bitmap.Width, item->Bitmap.Height); } return 0; }
/** * @brief Parse a @a button definition. * * Syntax: button=image,x,y,width,height,message * * @param in definition to be analyzed * * @return 0 (ok) or 1 (error) */ static int item_button(char *in) { unsigned char fname[256]; unsigned char file[512]; int x, y, w, h, message; char msg[32]; wItem *item; if (!window_item("button")) return 1; if (in_window("video")) return 1; if (in_window("menu")) return 1; cutItem(in, fname, ',', 0); x = cutItemToInt(in, ',', 1); y = cutItemToInt(in, ',', 2); w = cutItemToInt(in, ',', 3); h = cutItemToInt(in, ',', 4); cutItem(in, msg, ',', 5); message = appFindMessage(msg); if (message == -1) { skin_error(MSGTR_SKIN_UnknownMessage, msg); return 1; } mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] button image: %s %d,%d\n", fname, x, y); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] message: %s (#%d)\n", msg, message); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] size: %dx%d\n", w, h); item = next_item(); if (!item) return 1; item->type = itButton; item->x = x; item->y = y; item->width = w; item->height = h; item->message = message; item->pressed = btnReleased; if (item->message == evPauseSwitchToPlay) item->pressed = btnDisabled; item->Bitmap.Image = NULL; if (strcmp(fname, "NULL") != 0) { av_strlcpy(file, path, sizeof(file)); av_strlcat(file, fname, sizeof(file)); if (skinImageRead(file, &item->Bitmap) != 0) return 1; mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] (bitmap: %lux%lu)\n", item->Bitmap.Width, item->Bitmap.Height); } return 0; }
/** * @brief Read and parse a skin. * * @param sname name of the skin * * @return 0 (ok), -1 (skin file not found or not readable) or -2 (parsing error) */ int skinRead(char *sname) { char *skinfname; FILE *skinfile; unsigned char line[256]; unsigned char item[32]; unsigned char param[256]; unsigned int i; skinfname = setname(skinDirInHome, sname); if ((skinfile = fopen(skinfname, "rt")) == NULL) { skinfname = setname(skinMPlayerDir, sname); if ((skinfile = fopen(skinfname, "rt")) == NULL) { mp_msg(MSGT_GPLAYER, MSGL_ERR, MSGTR_SKIN_SkinFileNotFound, skinfname); return -1; } } mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] configuration file: %s\n", skinfname); appFreeStruct(); skin = NULL; currWinName[0] = 0; linenumber = 0; while (fgetstr(line, sizeof(line), skinfile)) { linenumber++; strswap(line, '\t', ' '); trim(line); decomment(line); if (!*line) continue; cutItem(line, item, '=', 0); cutItem(line, param, '=', 1); strlower(item); for (i = 0; i < FF_ARRAY_ELEMS(skinItem); i++) { if (!strcmp(item, skinItem[i].name)) { if (skinItem[i].func(param) != 0) return -2; else break; } } if (i == FF_ARRAY_ELEMS(skinItem)) { skin_error(MSGTR_SKIN_UNKNOWN_ITEM, item); return -2; } } if (linenumber == 0) { mp_msg(MSGT_GPLAYER, MSGL_ERR, MSGTR_SKIN_SkinFileNotReadable, skinfname); return -1; } return 0; }
struct skin_element* skin_parse(const char* document, skin_callback cb, void* cb_data) { callback = cb; callback_data = cb_data; #else struct skin_element* skin_parse(const char* document) { #endif struct skin_element* root = NULL; struct skin_element* last = NULL; const char* cursor = document; /*Keeps track of location in the document*/ skin_line = 1; skin_start = (char*)document; viewport_line = 0; skin_clear_errors(); while(*cursor != '\0') { struct skin_element* tree = skin_parse_viewport(&cursor); if(!root) { root = tree; last = root; } else { last->next = skin_buffer_to_offset(tree); last = tree; } if(!last) { skin_free_tree(root); /* Clearing any memory already used */ return NULL; } /* Making sure last is at the end */ while(IS_VALID_OFFSET(last->next)) last = skin_buffer_from_offset(last->next); } return root; } static struct skin_element* skin_parse_viewport(const char** document) { struct skin_element* root = NULL; struct skin_element* last = NULL; struct skin_element* retval = NULL; retval = skin_alloc_element(); if (!retval) return NULL; retval->type = VIEWPORT; retval->children_count = 1; retval->line = skin_line; viewport_line = skin_line; OFFSETTYPE(struct skin_element*)* children; const char* cursor = *document; /* Keeps track of location in the document */ const char* bookmark; /* Used when we need to look ahead */ int sublines = 0; /* Flag for parsing sublines */ /* Parsing out the viewport tag if there is one */ if(check_viewport(cursor)) { if (!skin_parse_tag(retval, &cursor)) return NULL; if(*cursor == '\n') { cursor++; skin_line++; } } #ifdef ROCKBOX else if (callback) { if (callback(retval, callback_data) == CALLBACK_ERROR) { skin_error(GOT_CALLBACK_ERROR, cursor); return NULL; } } #endif if (check_viewport(cursor)) { retval->children_count = 0; *document = cursor; return retval; } retval->children_count = 1; children = skin_alloc_children(1); if (!children) return NULL; do { /* First, we check to see if this line will contain sublines */ bookmark = cursor; sublines = 0; while(*cursor != '\n' && *cursor != '\0' && !(check_viewport(cursor) && cursor != *document)) { if(*cursor == MULTILINESYM) { sublines = 1; break; } else if(*cursor == TAGSYM) { skip_tag(&cursor); } else if(*cursor == COMMENTSYM) { skip_comment(&cursor); } else { /* Advancing the cursor as normal */ cursor++; } } cursor = bookmark; if(sublines) { struct skin_element* out = skin_parse_sublines(&cursor); if (!root) { root = out; last = root; } else { last->next = skin_buffer_to_offset(out); last = out; } if(!last) return NULL; } else { #ifdef ROCKBOX /* strip all leading comments */ while(*cursor == '#') { skip_comment(&cursor); skin_line++; } if (check_viewport(cursor)) break; #endif struct skin_element* out = skin_parse_line(&cursor); if (!root) { root = out; last = root; } else { last->next = skin_buffer_to_offset(out); last = out; } if(!last) return NULL; } /* Making sure last is at the end */ while(IS_VALID_OFFSET(last->next)) last = skin_buffer_from_offset(last->next); if(*cursor == '\n') { cursor++; skin_line++; } #ifdef ROCKBOX /* strip all comments */ while(*cursor == '#') { skip_comment(&cursor); skin_line++; } if (check_viewport(cursor)) break; #endif } while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document)); *document = cursor; children[0] = skin_buffer_to_offset(root); retval->children = skin_buffer_to_offset(children); return retval; } /* Auxiliary Parsing Functions */ static struct skin_element* skin_parse_line(const char**document) { return skin_parse_line_optional(document, 0); } /* * If conditional is set to true, then this will break upon encountering * SEPARATESYM. This should only be used when parsing a line inside a * conditional, otherwise just use the wrapper function skin_parse_line() */ static struct skin_element* skin_parse_line_optional(const char** document, int conditional) { const char* cursor = *document; struct skin_element* root = NULL; struct skin_element* current = NULL; struct skin_element* retval = NULL; OFFSETTYPE(struct skin_element*)* children = NULL; /* A wrapper for the line */ retval = skin_alloc_element(); if (!retval) return NULL; retval->type = LINE; retval->line = skin_line; while (*cursor == '\t') cursor++; if(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM && !(conditional && (*cursor == ARGLISTSEPARATESYM || *cursor == ARGLISTCLOSESYM || *cursor == ENUMLISTSEPARATESYM || *cursor == ENUMLISTCLOSESYM))) { retval->children_count = 1; } else { retval->children_count = 0; } if(retval->children_count > 0) { children = skin_alloc_children(1); if (!children) return NULL; } #ifdef ROCKBOX if (callback) { switch (callback(retval, callback_data)) { case CALLBACK_ERROR: skin_error(GOT_CALLBACK_ERROR, cursor); return NULL; default: break; } } #endif while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM && !((*cursor == ARGLISTSEPARATESYM || *cursor == ARGLISTCLOSESYM || *cursor == ENUMLISTSEPARATESYM || *cursor == ENUMLISTCLOSESYM) && conditional) && !(check_viewport(cursor) && cursor != *document)) { /* Allocating memory if necessary */ if(root) { struct skin_element *next = skin_alloc_element(); if (!next) return NULL; current->next = skin_buffer_to_offset(next); current = next; } else { current = skin_alloc_element(); if (!current) return NULL; root = current; } /* Parsing the current element */ if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM) { if(!skin_parse_conditional(current, &cursor)) return NULL; } else if(*cursor == TAGSYM && !find_escape_character(cursor[1])) { if(!skin_parse_tag(current, &cursor)) return NULL; } else if(*cursor == COMMENTSYM) { if(!skin_parse_comment(current, &cursor)) return NULL; } else { if(!skin_parse_text(current, &cursor, conditional)) return NULL; } } /* Moving up the calling function's pointer */ *document = cursor; if(root) { children[0] = skin_buffer_to_offset(root); retval->children = skin_buffer_to_offset(children); } return retval; } static struct skin_element* skin_parse_sublines(const char** document) { return skin_parse_sublines_optional(document, 0); } static struct skin_element* skin_parse_sublines_optional(const char** document, int conditional) { struct skin_element* retval; OFFSETTYPE(struct skin_element*)* children; const char* cursor = *document; int sublines = 1; int i; retval = skin_alloc_element(); if (!retval) return NULL; retval->type = LINE_ALTERNATOR; retval->next = skin_buffer_to_offset(NULL); retval->line = skin_line; while (*cursor == '\t') cursor++; /* First we count the sublines */ while(*cursor != '\0' && *cursor != '\n' && !((*cursor == ARGLISTSEPARATESYM || *cursor == ARGLISTCLOSESYM || *cursor == ENUMLISTSEPARATESYM || *cursor == ENUMLISTCLOSESYM) && conditional) && !(check_viewport(cursor) && cursor != *document)) { if(*cursor == COMMENTSYM) { skip_comment(&cursor); } else if(*cursor == TAGSYM) { skip_tag(&cursor); } else if(*cursor == MULTILINESYM) { sublines++; cursor++; } else { cursor++; } } /* ...and then we parse them */ retval->children_count = sublines; children = skin_alloc_children(sublines); if (!children) return NULL; cursor = *document; for(i = 0; i < sublines; i++) { children[i] = skin_buffer_to_offset(skin_parse_line_optional(&cursor, conditional)); if (children[i] < 0) return NULL; skip_whitespace(&cursor); if(*cursor != MULTILINESYM && i != sublines - 1) { skin_error(MULTILINE_EXPECTED, cursor); return NULL; } else if(i != sublines - 1) { cursor++; } } #ifdef ROCKBOX if (callback) { if (callback(retval, callback_data) == CALLBACK_ERROR) { skin_error(GOT_CALLBACK_ERROR, *document); return NULL; } } #endif *document = cursor; retval->children = skin_buffer_to_offset(children); return retval; } static int skin_parse_tag(struct skin_element* element, const char** document) { const char* cursor = *document + 1; const char* bookmark; char *open_square_bracket = NULL; char tag_name[MAX_TAG_LENGTH]; char* tag_args; const struct tag_info *tag; struct skin_tag_parameter* params = NULL; int num_args = 1; int i; int qmark = 0; /* Flag for the all-or-none option */ int optional = 0; /* Checking the tag name */ for (i=0; cursor[i] && i<MAX_TAG_LENGTH; i++) tag_name[i] = cursor[i]; /* First we check the two characters after the '%', then a single char */ tag = NULL; i = MAX_TAG_LENGTH; while (!tag && i > 1) { tag_name[i-1] = '\0'; tag = find_tag(tag_name); i--; } if(!tag) { skin_error(ILLEGAL_TAG, cursor); return 0; } cursor += i; /* Copying basic tag info */ if(element->type != CONDITIONAL && element->type != VIEWPORT) element->type = TAG; element->tag = tag; tag_args = tag->params; element->line = skin_line; /* Checking for the * flag */ if(tag_args[0] == '?') { qmark = 1; tag_args++; } /* If this tag has no arguments, we can bail out now */ if(strlen(tag_args) == 0 || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM) || (qmark && *cursor != ARGLISTOPENSYM)) { #ifdef ROCKBOX if (callback) { if (callback(element, callback_data) == CALLBACK_ERROR) { skin_error(GOT_CALLBACK_ERROR, cursor); return 0; } } #endif *document = cursor; return 1; } /* Checking the number of arguments and allocating args */ if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|') { skin_error(ARGLIST_EXPECTED, cursor); return 0; } else { cursor++; } bookmark = cursor; while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM) { /* Skipping over escaped characters */ if(*cursor == TAGSYM && *(cursor+1) != ARGLISTSEPARATESYM) { skip_tag(&cursor); } else if(*cursor == COMMENTSYM) { skip_comment(&cursor); } else if(*cursor == ARGLISTSEPARATESYM) { num_args++; cursor++; } else { cursor++; } } cursor = bookmark; /* Restoring the cursor */ element->params_count = num_args; params = skin_alloc_params(num_args); if (!params) return 0; /* Now we have to actually parse each argument */ for(i = 0; i < num_args; i++) { char type_code; /* Making sure we haven't run out of arguments */ if(*tag_args == '\0') { skin_error(TOO_MANY_ARGS, cursor); return 0; } /* Checking for the optional bar */ if(*tag_args == '|') { optional = 1; tag_args++; } /* Scanning the arguments */ skip_whitespace(&cursor); /* Checking for comments */ if(*cursor == COMMENTSYM) skip_comment(&cursor); if (*tag_args == '[') { /* we need to guess which type of param it is. * guess using this priority: * default > decimal/integer > single tag/code > string */ int j=0; bool canbedefault = false, last_char_is_percent = false; bool haspercent = false, number = true, hasdecimal = false; char temp_params[8]; open_square_bracket = tag_args; tag_args++; while (*tag_args != ']') { if (*tag_args >= 'a' && *tag_args <= 'z') canbedefault = true; temp_params[j++] = tolower(*tag_args++); } temp_params[j] = '\0'; j = 0; while (cursor[j] && cursor[j] != ',' && cursor[j] != ')') { haspercent = haspercent || (cursor[j] == '%'); hasdecimal = hasdecimal || (cursor[j] == '.'); number = number && (isdigit(cursor[j]) || (cursor[j] == '.') || (cursor[j] == '-') || (cursor[j] == '%')); j++; } last_char_is_percent = cursor[j-1] == '%'; type_code = '?'; if (canbedefault && *cursor == DEFAULTSYM && !isdigit(cursor[1])) { type_code = 'i'; } else if (number && hasdecimal && strchr(temp_params, 'd')) { type_code = 'd'; } else if (number && last_char_is_percent && strchr(temp_params, 'p')) { type_code = 'p'; } else if (number && (strchr(temp_params, 'i') || strchr(temp_params, 'd'))) { type_code = strchr(temp_params, 'i') ? 'i' : 'd'; } else if (haspercent && (strchr(temp_params, 't') || strchr(temp_params, 'c'))) { type_code = strchr(temp_params, 't') ? 't' : 'c'; } else if (strchr(temp_params, 's')) { type_code = 's'; } if (type_code == '?') { skin_error(INSUFFICIENT_ARGS, cursor); return 0; } } else type_code = *tag_args; /* Storing the type code */ params[i].type_code = type_code; /* Checking a nullable argument for null. */ if(*cursor == DEFAULTSYM && !isdigit(cursor[1])) { if(islower(type_code)) { params[i].type = DEFAULT; cursor++; } else { skin_error(DEFAULT_NOT_ALLOWED, cursor); return 0; } } else if(tolower(type_code) == 'i') { /* Scanning an int argument */ if(!isdigit(*cursor) && *cursor != '-') { skin_error(INT_EXPECTED, cursor); return 0; } params[i].type = INTEGER; params[i].data.number = scan_int(&cursor); } else if(tolower(type_code) == 'd' || tolower(type_code) == 'p') { int val = 0; bool have_point = false; bool have_tenth = false; while ( isdigit(*cursor) || *cursor == '.' ) { if (*cursor != '.') { val *= 10; val += *cursor - '0'; if (have_point) { have_tenth = true; cursor++; break; } } else have_point = true; cursor++; } if (have_tenth == false) val *= 10; if (tolower(type_code) == 'd') params[i].type = DECIMAL; else { params[i].type = PERCENT; cursor++; /* skip trailing % sign */ } params[i].data.number = val; } else if(tolower(type_code) == 's' || tolower(type_code) == 'f') { /* Scanning a string argument */ params[i].type = STRING; params[i].data.text = skin_buffer_to_offset(scan_string(&cursor)); } else if(tolower(type_code) == 'c') { /* Recursively parsing a code argument */ params[i].type = CODE; params[i].data.code = skin_buffer_to_offset(skin_parse_code_as_arg(&cursor)); if(params[i].data.code < 0) return 0; } else if (tolower(type_code) == 't') { struct skin_element* child = skin_alloc_element(); child->type = TAG; if (!skin_parse_tag(child, &cursor)) return 0; child->next = skin_buffer_to_offset(NULL); params[i].type = CODE; params[i].data.code = skin_buffer_to_offset(child); } skip_whitespace(&cursor); if(*cursor != ARGLISTSEPARATESYM && i < num_args - 1) { skin_error(SEPARATOR_EXPECTED, cursor); return 0; } else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1) { skin_error(CLOSE_EXPECTED, cursor); return 0; } else { cursor++; } if (*(tag_args + 1) == '*') { if (i+1 == num_args) tag_args += 2; else if (open_square_bracket && *tag_args == ']') { tag_args = open_square_bracket; open_square_bracket = NULL; } } else tag_args++; /* Checking for the optional bar */ if(*tag_args == '|') { optional = 1; tag_args++; } } element->params = skin_buffer_to_offset(params); /* Checking for a premature end */ if(*tag_args != '\0' && !optional) { skin_error(INSUFFICIENT_ARGS, cursor); return 0; } #ifdef ROCKBOX if (callback) { if (callback(element, callback_data) == CALLBACK_ERROR) { skin_error(GOT_CALLBACK_ERROR, *document); return 0; } } #endif *document = cursor; return 1; } /* * If the conditional flag is set true, then parsing text will stop at an * ARGLISTSEPARATESYM. Only set that flag when parsing within a conditional */ static int skin_parse_text(struct skin_element* element, const char** document, int conditional) { const char* cursor = *document; int length = 0; int dest; char *text = NULL; /* First figure out how much text we're copying */ while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM && *cursor != COMMENTSYM && !((*cursor == ARGLISTSEPARATESYM || *cursor == ARGLISTCLOSESYM || *cursor == ENUMLISTSEPARATESYM || *cursor == ENUMLISTCLOSESYM) && conditional)) { /* Dealing with possibility of escaped characters */ if(*cursor == TAGSYM) { if(find_escape_character(cursor[1])) cursor++; else break; } length++; cursor++; } cursor = *document; /* Copying the text into the element struct */ element->type = TEXT; element->line = skin_line; element->next = skin_buffer_to_offset(NULL); text = skin_alloc_string(length); element->data = skin_buffer_to_offset(text); if (element->data < 0) return 0; for(dest = 0; dest < length; dest++) { /* Advancing cursor if we've encountered an escaped character */ if(*cursor == TAGSYM) cursor++; text[dest] = *cursor; cursor++; } text[length] = '\0'; #ifdef ROCKBOX if (callback) { if (callback(element, callback_data) == CALLBACK_ERROR) { skin_error(GOT_CALLBACK_ERROR, *document); return 0; } } #endif *document = cursor; return 1; } static int skin_parse_conditional(struct skin_element* element, const char** document) { const char* cursor = *document + 1; /* Starting past the "%" */ const char* bookmark; int children = 1; int i; #ifdef ROCKBOX bool feature_available = true; const char *false_branch = NULL; const char *conditional_end = NULL; #endif OFFSETTYPE(struct skin_element*)* children_array = NULL; /* Some conditional tags allow for target feature checking, * so to handle that call the callback as usual with type == TAG * then call it a second time with type == CONDITIONAL and check the return * value */ element->type = TAG; element->line = skin_line; /* Parsing the tag first */ if(!skin_parse_tag(element, &cursor)) return 0; element->type = CONDITIONAL; #ifdef ROCKBOX if (callback) { switch (callback(element, callback_data)) { case FEATURE_NOT_AVAILABLE: feature_available = false; break; case CALLBACK_ERROR: return 0; default: break; } } #endif /* Counting the children */ if(*(cursor++) != ENUMLISTOPENSYM) { skin_error(ARGLIST_EXPECTED, cursor); return 0; } bookmark = cursor; while(*cursor != ENUMLISTCLOSESYM && *cursor != '\0') { if(*cursor == COMMENTSYM) { skip_comment(&cursor); } else if(*cursor == TAGSYM) { skip_tag(&cursor); } else if(*cursor == ENUMLISTSEPARATESYM) { children++; cursor++; if (*cursor == '\n') cursor++; #ifdef ROCKBOX if (false_branch == NULL && !feature_available) { false_branch = cursor; children--; } #endif } else { cursor++; } } #ifdef ROCKBOX if (*cursor == ENUMLISTCLOSESYM && false_branch == NULL && !feature_available) { false_branch = cursor+1; children--; } if (element->tag->flags&FEATURE_TAG) { if (feature_available && children > 1) children--; } conditional_end = cursor; /* if we are skipping the true branch fix that up */ cursor = false_branch ? false_branch : bookmark; #else cursor = bookmark; #endif /* Parsing the children */ /* Feature tags could end up having 0 children which breaks * the render in dangerous ways. Minor hack, but insert an empty * child. (e.g %?xx<foo> when xx isnt available ) */ if (children == 0) { const char* emptyline= ""; children = 1; children_array = skin_alloc_children(children); if (!children_array) return 0; element->children_count = children; children_array[0] = skin_buffer_to_offset(skin_parse_code_as_arg(&emptyline)); } else { children_array = skin_alloc_children(children); if (!children_array) return 0; element->children_count = children; for(i = 0; i < children; i++) { if (*cursor == '\n') { skin_line++; cursor++; } children_array[i] = skin_buffer_to_offset(skin_parse_code_as_arg(&cursor)); if (children_array[i] < 0) return 0; skip_whitespace(&cursor); #ifdef ROCKBOX if ((element->tag->flags&FEATURE_TAG) && feature_available) cursor = conditional_end; #endif if(i < children - 1 && *cursor != ENUMLISTSEPARATESYM) { skin_error(SEPARATOR_EXPECTED, cursor); return 0; } else if(i == children - 1 && *cursor != ENUMLISTCLOSESYM) { skin_error(CLOSE_EXPECTED, cursor); return 0; } else { cursor++; } } } *document = cursor; element->children = skin_buffer_to_offset(children_array); return 1; } static int skin_parse_comment(struct skin_element* element, const char** document) { const char* cursor = *document; #ifndef ROCKBOX char* text = NULL; #endif int length; /* * Finding the index of the ending newline or null-terminator * The length of the string of interest doesn't include the leading #, the * length we need to reserve is the same as the index of the last character */ for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++); element->type = COMMENT; element->line = skin_line; #ifdef ROCKBOX element->data = INVALID_OFFSET; #else element->data = text = skin_alloc_string(length); if (!element->data) return 0; /* We copy from one char past cursor to leave out the # */ memcpy((void*)text, (void*)(cursor + 1), sizeof(char) * (length-1)); text[length - 1] = '\0'; #endif if(cursor[length] == '\n') skin_line++; *document += (length); /* Move cursor up past # and all text */ if(**document == '\n') (*document)++; return 1; } static struct skin_element* skin_parse_code_as_arg(const char** document) { int sublines = 0; const char* cursor = *document; /* Checking for sublines */ while(*cursor != '\n' && *cursor != '\0' && *cursor != ENUMLISTSEPARATESYM && *cursor != ARGLISTSEPARATESYM && *cursor != ENUMLISTCLOSESYM && *cursor != ARGLISTCLOSESYM) { if(*cursor == MULTILINESYM) { sublines = 1; break; } else if(*cursor == TAGSYM) { skip_tag(&cursor); } else { /* Advancing the cursor as normal */ cursor++; } } if(sublines) return skin_parse_sublines_optional(document, 1); else return skin_parse_line_optional(document, 1); }
/** * @brief Parse a @a base definition. * * Syntax: base=image,x,y[,width,height] * * @param in definition to be analyzed * * @return 0 (ok) or 1 (error) */ static int item_base(char *in) { unsigned char fname[256]; unsigned char file[512]; int x, y; int w = 0, h = 0; int is_video, is_bar, is_menu; if (!window_item("base")) return 1; is_video = (strcmp(currWinName, "video") == 0); is_bar = (strcmp(currWinName, "playbar") == 0); is_menu = (strcmp(currWinName, "menu") == 0); cutItem(in, fname, ',', 0); x = cutItemToInt(in, ',', 1); y = cutItemToInt(in, ',', 2); w = cutItemToInt(in, ',', 3); h = cutItemToInt(in, ',', 4); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] image: %s", fname); currWin->type = itBase; if (!is_menu) { currWin->x = x; currWin->y = y; mp_msg(MSGT_GPLAYER, MSGL_DBG2, " %d,%d", x, y); } mp_msg(MSGT_GPLAYER, MSGL_DBG2, "\n"); av_strlcpy(file, path, sizeof(file)); av_strlcat(file, fname, sizeof(file)); if (skinImageRead(file, &currWin->Bitmap) != 0) return 1; currWin->width = currWin->Bitmap.Width; currWin->height = currWin->Bitmap.Height; if (is_video) { if (w && h) { currWin->width = w; currWin->height = h; } } mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] bitmap: %dx%d\n", currWin->width, currWin->height); if (!is_video) { if (!bpRenderMask(&currWin->Bitmap, &currWin->Mask)) { skin_error(MSGTR_SKIN_NotEnoughMemory); return 1; } mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[skin] mask: %lux%lu\n", currWin->Mask.Width, currWin->Mask.Height); } if (is_bar) skin->playbarIsPresent = 1; if (is_menu) skin->menuIsPresent = 1; return 0; }