blob: 09f2c8dd123f3769f48e4af99936a14f6f6a3f07 [file] [log] [blame]
/*$off*/
// $Id: utils.cpp,v 1.26 2002/07/02 22:04:36 t1mpy Exp $
// id3lib: a C++ library for creating and manipulating id3v1/v2 tags
// Copyright 1999, 2000 Scott Thomas Haug
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by
// the Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
// License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with this library; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// The id3lib authors encourage improvements and optimisations to be sent to
// the id3lib coordinator. Please see the README file for details on where to
// send such submissions. See the AUTHORS file for a list of people who have
// contributed to id3lib. See the ChangeLog file for a list of changes to
// id3lib. These files are distributed with id3lib at
// http://download.sourceforge.net/id3lib/
/*$on*/
#include <ctype.h>
#include <errno.h>
#include "id3/utils.h" // has <config.h> "id3/id3lib_streams.h" "id3/globals.h" "id3/id3lib_strings.h"
#if (defined(__GNUC__) && __GNUC__ == 2)
#define NOCREATE ios::nocreate
#else
#define NOCREATE ((std::ios_base::openmode) 0)
#endif
#ifdef WIN32
#include <windows.h>
#else
#define min(a, b) (a < b ? a : b)
#endif
using namespace dami;
// ESL: The original code is complicated, but maybe supports a wider range of platforms? The changes hereafter
// leverage Unicode utilities and known shortcuts between encodings.
#include "../unicode.org/ConvertUTF.c"
/* =====================================================================================================================
======================================================================================================================= */
inline bool _legalTagChar(int c)
{
// NULL, TAB, CR, LF and anything higher or equal to space is legal
return !c || (c >= 0x20) || (c == 0x09) || (c == 0x0a) || (c == 0x0d);
}
/* =====================================================================================================================
======================================================================================================================= */
String Utf8FromLatin1String(const char *input, size_t src_sz)
{
String convertedString("");
// check the input
const unsigned char *src_buf = (const unsigned char *) input;
if(!src_buf || !src_sz) {
return convertedString;
}
// Allocate buffer and perform conversion. Since input is Latin1 it never expands to more than 2 octets
size_t dest_sz = src_sz * 2;
unsigned char *dest_buf = new unsigned char[dest_sz + 2];
unsigned char *dest_ptr = dest_buf;
int c = (int) *((const unsigned char *) src_buf);
while(c && src_sz--) {
if(c >= 128) {
*dest_ptr++ = (char) (((c & 0x7c0) >> 6) | 0xc0);
*dest_ptr++ = (char) ((c & 0x3f) | 0x80);
}
else if(!_legalTagChar(c)) {
*dest_ptr++ = '?';
}
else {
*dest_ptr++ = (char) c;
}
c = (int) *((const unsigned char *)++src_buf);
}
size_t length = dest_ptr - dest_buf;
if(length) {
dest_buf[length] = dest_buf[length + 1] = '\0';
convertedString = String((const char *) dest_buf);
}
delete[] dest_buf;
return convertedString;
}
/* =====================================================================================================================
======================================================================================================================= */
String Utf8FromUtf16String(const unicode_t *input, size_t src_sz)
{
String convertedString("");
// check the input
const UTF16 *src_buf = (const UTF16 *) input;
if(!src_buf || !src_sz || ((src_sz % 2) != 0)) {
return convertedString;
}
// allocate buffer and perform conversion. At most each UCS2 expands to 3 octets in UTF8, but since this is
// physical length in octet alreay
size_t dest_sz = (size_t) (((float) src_sz) * 1.5) + 1;
unsigned char *dest_buf = new unsigned char[dest_sz + 2];
unsigned char *dest_ptr = dest_buf;
ConversionResult result = ConvertUTF16toUTF8((const UTF16 **) &src_buf, ((const UTF16 *) src_buf) + (src_sz / sizeof(UTF16)), (UTF8 **) &dest_ptr,
((UTF8 *) dest_buf) + dest_sz, strictConversion);
// On success set the string in _data
if(result == conversionOK) {
size_t length = dest_ptr - dest_buf; // physical length
dest_buf[length] = dest_buf[length + 1] = '\0';
convertedString = String((const char *) dest_buf);
}
delete[] dest_buf;
return convertedString;
}
/* =====================================================================================================================
======================================================================================================================= */
String Latin1FromUtf8String(const char *input, size_t src_sz)
{
String convertedString("");
// check the input
const unsigned char *src_buf = (const unsigned char *) input;
if(!src_buf || !src_sz) {
return convertedString;
}
// allocate buffer. The latin1 string will not be larger than the UTF-8 one
unsigned char *dest_buf = new unsigned char[src_sz + 2];
unsigned char *dest_ptr = dest_buf;
// perform conversion
int c = (int) *src_buf;
while(c && src_sz--) {
if(c >= 128) {
int c2 = (int) * ++src_buf;
if(!c2) {
break; // We reached the end of string
}
// conversion rule: 110yyyyy(C2-DF) 10zzzzzz(80-BF);
// However we really only care about 2 of the 'y'
*dest_ptr++ = (unsigned char) (((c & 0x3) << 6) + (c2 & 0x3F));
// Decrement counter since we consumed one more character
src_sz--;
}
else if(!_legalTagChar(c)) {
*dest_ptr++ = (unsigned char) '?';
}
else {
*dest_ptr++ = (unsigned char) c;
}
c = (int) * ++src_buf;
}
size_t length = dest_ptr - dest_buf;
dest_buf[length] = dest_buf[length + 1] = '\0';
convertedString = String((const char *) dest_buf);
delete[] dest_buf;
return convertedString;
}
/* =====================================================================================================================
======================================================================================================================= */
String Latin1FromUtf16String(const unicode_t *input, size_t src_sz)
{
String convertedString("");
// check the input
const UTF16 *src_buf = (const UTF16 *) input;
if(!src_buf || !src_sz || ((src_sz % 2) != 0)) {
return convertedString;
}
// Each UCS2 character hanles one Latin1 character. But takes half the physical space
size_t dest_sz = src_sz / sizeof(UTF16);
size_t length = src_sz / sizeof(UTF16);
unsigned char *dest_buf = new unsigned char[dest_sz + 2];
unsigned char *dest_ptr = dest_buf;
while(dest_sz--) {
unsigned char c = (unsigned char) (0x00FF & (*src_buf++));
if(!_legalTagChar(c)) {
*dest_ptr++ = '?';
}
else {
*dest_ptr++ = c;
}
}
dest_buf[length] = dest_buf[length + 1] = '\0';
convertedString = String((const char *) dest_buf);
delete[] dest_buf;
return convertedString;
}
/* =====================================================================================================================
======================================================================================================================= */
String Utf16FromLatin1String(const char *input, size_t src_sz)
{
String convertedString("");
// check the input
const unsigned char *src_buf = (const unsigned char *) input;
if(!src_buf || !src_sz) {
return convertedString;
}
// allocate the buffer
size_t length = src_sz * sizeof(UTF16);
unsigned char *dest_buf = new unsigned char[length + 2];
UTF16 *dest_ptr = (UTF16 *) dest_buf;
while(src_sz--) {
if(!_legalTagChar(*src_buf)) {
*dest_ptr++ = (UTF16) '?';
src_buf++;
}
else {
*dest_ptr++ = (UTF16) * src_buf++;
}
}
dest_buf[length] = dest_buf[length + 1] = '\0';
convertedString = String((const char *) dest_buf, length + 2);
delete[] dest_buf;
return convertedString;
}
/* =====================================================================================================================
======================================================================================================================= */
String Utf16FromUtf8String(const char *input, size_t src_sz)
{
String convertedString("");
// check the input
const unsigned char *src_buf = (const unsigned char *) input;
if(!src_buf || !src_sz) {
return convertedString;
}
// allocate buffer and perform conversion
size_t dest_sz = src_sz;
unsigned char *dest_buf = new unsigned char[(dest_sz + 1) * sizeof(UTF16)];
unsigned char *dest_ptr = dest_buf;
ConversionResult result = ConvertUTF8toUTF16((const UTF8 **) &src_buf, (const UTF8 *) (src_buf + src_sz), (UTF16 **) &dest_ptr,
((UTF16 *) dest_buf) + dest_sz, strictConversion);
// On success set the string in _data
if(result == conversionOK) {
size_t length = dest_ptr - dest_buf; // physical length
dest_buf[length] = dest_buf[length + 1] = '\0';
convertedString = String((const char *) dest_buf, length + 2);
}
delete[] dest_buf;
return convertedString;
}
/* =====================================================================================================================
======================================================================================================================= */
size_t dami::renderNumber(uchar *buffer, uint32 val, size_t size)
{
uint32 num = val;
for(size_t i = 0; i < size; i++) {
buffer[size - i - 1] = (uchar) (num & MASK8);
num >>= 8;
}
return size;
}
/* =====================================================================================================================
======================================================================================================================= */
String dami::renderNumber(uint32 val, size_t size)
{
String str(size, '\0');
uint32 num = val;
for(size_t i = 0; i < size; i++) {
str[size - i - 1] = (uchar) (num & MASK8);
num >>= 8;
}
return str;
}
// =====================================================================================================================
// this should be convert( const String&, ...) to be changed soon...
// =====================================================================================================================
String dami::convert(String data, ID3_TextEnc sourceEnc, ID3_TextEnc targetEnc)
{
if((sourceEnc == targetEnc) || (data.size() == 0)) {
return String(data);
}
switch(sourceEnc) {
case ID3TE_ISO8859_1:
{
size_t len = data.length();
if(targetEnc == ID3TE_UTF8) {
return Utf8FromLatin1String(data.c_str(), len);
}
else if(targetEnc == ID3TE_UTF16) {
return Utf16FromLatin1String(data.c_str(), len);
}
}
break;
case ID3TE_UTF8:
{
size_t len = data.length();
if(targetEnc == ID3TE_ISO8859_1) {
return Latin1FromUtf8String(data.c_str(), len);
}
else if(targetEnc == ID3TE_UTF16) {
return Utf16FromUtf8String(data.c_str(), len);
}
}
break;
case ID3TE_UTF16:
{
size_t len = ucslen((const unicode_t *) data.c_str()) * sizeof(unicode_t);
if(targetEnc == ID3TE_ISO8859_1) {
return Latin1FromUtf16String((unicode_t *) data.c_str(), len);
}
else if(targetEnc == ID3TE_UTF8) {
return Utf8FromUtf16String((unicode_t *) data.c_str(), len);
}
}
break;
}
return "";
}
/* =====================================================================================================================
======================================================================================================================= */
size_t dami::ucslen(const unicode_t *unicode)
{
if(NULL != unicode) {
for(size_t size = 0; true; size++) {
if(NULL_UNICODE == unicode[size]) {
return size;
}
}
}
return 0;
}
namespace
{
/* =================================================================================================================
=================================================================================================================== */
bool exists(String name)
{
ifstream file(name.c_str(), NOCREATE);
return file.is_open() != 0;
}
};
/* =====================================================================================================================
======================================================================================================================= */
ID3_Err dami::createFile(String name, fstream &file)
{
if(file.is_open()) {
file.close();
}
file.open(name.c_str(), ios::in | ios::out | ios::binary | ios::trunc);
if(!file) {
return ID3E_ReadOnly;
}
return ID3E_NoError;
}
/* =====================================================================================================================
======================================================================================================================= */
size_t dami::getFileSize(fstream &file)
{
size_t size = 0;
if(file.is_open()) {
streamoff curpos = file.tellg();
file.seekg(0, ios::end);
size = file.tellg();
file.seekg(curpos);
}
return size;
}
/* =====================================================================================================================
======================================================================================================================= */
size_t dami::getFileSize(ifstream &file)
{
size_t size = 0;
if(file.is_open()) {
streamoff curpos = file.tellg();
file.seekg(0, ios::end);
size = file.tellg();
file.seekg(curpos);
}
return size;
}
/* =====================================================================================================================
======================================================================================================================= */
size_t dami::getFileSize(ofstream &file)
{
size_t size = 0;
if(file.is_open()) {
streamoff curpos = file.tellp();
file.seekp(0, ios::end);
size = file.tellp();
file.seekp(curpos);
}
return size;
}
/* =====================================================================================================================
======================================================================================================================= */
ID3_Err dami::openWritableFile(String name, fstream &file)
{
if(!exists(name)) {
return ID3E_NoFile;
}
if(file.is_open()) {
file.close();
}
file.open(name.c_str(), ios::in | ios::out | ios::binary | NOCREATE);
if(!file) {
return ID3E_ReadOnly;
}
return ID3E_NoError;
}
/* =====================================================================================================================
======================================================================================================================= */
ID3_Err dami::openWritableFile(String name, ofstream &file)
{
if(!exists(name)) {
return ID3E_NoFile;
}
if(file.is_open()) {
file.close();
}
file.open(name.c_str(), ios::in | ios::out | ios::binary | NOCREATE);
if(!file) {
return ID3E_ReadOnly;
}
return ID3E_NoError;
}
/* =====================================================================================================================
======================================================================================================================= */
ID3_Err dami::openReadableFile(String name, fstream &file)
{
if(file.is_open()) {
file.close();
}
file.open(name.c_str(), ios::in | ios::binary | NOCREATE);
if(!file) {
return ID3E_NoFile;
}
return ID3E_NoError;
}
#ifdef WIN32
ID3_Err dami:: openReadableFile(std::wstring name, ifstream &file)
#else
ID3_Err dami::openReadableFile(String name, ifstream &file)
#endif
{
if(file.is_open()) {
file.close();
}
file.open(name.c_str(), ios::in | ios::binary | NOCREATE);
if(!file) {
return ID3E_NoFile;
}
return ID3E_NoError;
}
// =====================================================================================================================
// wow, did we really had to implement string by ourself (ralf)
// =====================================================================================================================
String dami::toString(uint32 val)
{
if(val == 0) {
return "0";
}
String text;
while(val > 0) {
String tmp;
char ch = (val % 10) + '0';
tmp += ch;
text = tmp + text;
val /= 10;
}
return text;
}