static void decode2utf8(const unsigned char *src, unsigned char **dst, int srcsize, int *dstsize, int codepage) { unsigned char tmpbuf[srcsize * 3 + 1]; unsigned char *p; int utf8size; if (codepage < NUM_CODEPAGES) p = iso_decode(src, tmpbuf, codepage, srcsize); else /* codepage == UCS2 */ p = utf16BEdecode(src, tmpbuf, srcsize); *p = '\0'; strlcpy(*dst, tmpbuf, *dstsize); utf8size = (p - tmpbuf) + 1; if (utf8size > *dstsize) { DEBUGF("metadata warning: data length: %d > contents store buffer size: %d\n", utf8size, *dstsize); utf8size = *dstsize; } *dst += utf8size; *dstsize -= utf8size; }
/* Recode any UTF-16 string to UTF-8 */ unsigned char* utf16decode(const unsigned char *utf16, unsigned char *utf8, unsigned int count) { unsigned long ucs; ucs = *(utf16++) << 8; ucs |= *(utf16++); if (ucs == 0xFEFF) /* Check for BOM */ return utf16BEdecode(utf16, utf8, count-1); else if (ucs == 0xFFFE) return utf16LEdecode(utf16, utf8, count-1); else { /* ADDME: Should default be LE or BE? */ utf16 -= 2; return utf16BEdecode(utf16, utf8, count); } }
/* parse cuesheet "cue_file" and store the information in "cue" */ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) { char line[MAX_PATH]; char *s; unsigned char char_enc = CHAR_ENC_ISO_8859_1; bool is_embedded = false; int line_len; int bytes_left = 0; int read_bytes = MAX_PATH; unsigned char utf16_buf[MAX_PATH]; int fd = open(cue_file->path, O_RDONLY, 0644); if(fd < 0) return false; if (cue_file->pos > 0) { is_embedded = true; lseek(fd, cue_file->pos, SEEK_SET); bytes_left = cue_file->size; char_enc = cue_file->encoding; } /* Look for a Unicode BOM */ unsigned char bom_read = 0; read(fd, line, BOM_UTF_8_SIZE); if(!memcmp(line, BOM_UTF_8, BOM_UTF_8_SIZE)) { char_enc = CHAR_ENC_UTF_8; bom_read = BOM_UTF_8_SIZE; } else if(!memcmp(line, BOM_UTF_16_LE, BOM_UTF_16_SIZE)) { char_enc = CHAR_ENC_UTF_16_LE; bom_read = BOM_UTF_16_SIZE; } else if(!memcmp(line, BOM_UTF_16_BE, BOM_UTF_16_SIZE)) { char_enc = CHAR_ENC_UTF_16_BE; bom_read = BOM_UTF_16_SIZE; } if (bom_read < BOM_UTF_8_SIZE) lseek(fd, cue_file->pos + bom_read, SEEK_SET); if (is_embedded) { if (bom_read > 0) bytes_left -= bom_read; if (read_bytes > bytes_left) read_bytes = bytes_left; } /* Initialization */ memset(cue, 0, sizeof(struct cuesheet)); strcpy(cue->path, cue_file->path); cue->curr_track = cue->tracks; while ((line_len = read_line(fd, line, read_bytes)) > 0 && cue->track_count < MAX_TRACKS ) { if (char_enc == CHAR_ENC_UTF_16_LE) { s = utf16LEdecode(line, utf16_buf, line_len); /* terminate the string at the newline */ *s = '\0'; strcpy(line, utf16_buf); /* chomp the trailing 0 after the newline */ lseek(fd, 1, SEEK_CUR); line_len++; } else if (char_enc == CHAR_ENC_UTF_16_BE) { s = utf16BEdecode(line, utf16_buf, line_len); *s = '\0'; strcpy(line, utf16_buf); } s = skip_whitespace(line); if (!strncmp(s, "TRACK", 5)) { cue->track_count++; } else if (!strncmp(s, "INDEX 01", 8)) { s = strchr(s,' '); s = skip_whitespace(s); s = strchr(s,' '); s = skip_whitespace(s); cue->tracks[cue->track_count-1].offset = 60*1000 * atoi(s); s = strchr(s,':') + 1; cue->tracks[cue->track_count-1].offset += 1000 * atoi(s); s = strchr(s,':') + 1; cue->tracks[cue->track_count-1].offset += 13 * atoi(s); } else if (!strncmp(s, "TITLE", 5) || !strncmp(s, "PERFORMER", 9) || !strncmp(s, "SONGWRITER", 10)) { char *dest = NULL; char *string = get_string(s); if (!string) break; switch (*s) { case 'T': /* TITLE */ dest = (cue->track_count <= 0) ? cue->title : cue->tracks[cue->track_count-1].title; break; case 'P': /* PERFORMER */ dest = (cue->track_count <= 0) ? cue->performer : cue->tracks[cue->track_count-1].performer; break; case 'S': /* SONGWRITER */ dest = (cue->track_count <= 0) ? cue->songwriter : cue->tracks[cue->track_count-1].songwriter; break; } if (dest) { if (char_enc == CHAR_ENC_ISO_8859_1) { dest = iso_decode(string, dest, -1, MIN(strlen(string), MAX_NAME)); *dest = '\0'; } else { strlcpy(dest, string, MAX_NAME*3 + 1); } } } if (is_embedded) { bytes_left -= line_len; if (bytes_left <= 0) break; if (bytes_left < read_bytes) read_bytes = bytes_left; } } close(fd); /* If some songs don't have performer info, we copy the cuesheet performer */ int i; for (i = 0; i < cue->track_count; i++) { if (*(cue->tracks[i].performer) == '\0') strlcpy(cue->tracks[i].performer, cue->performer, MAX_NAME*3); if (*(cue->tracks[i].songwriter) == '\0') strlcpy(cue->tracks[i].songwriter, cue->songwriter, MAX_NAME*3); } return true; }