1108 lines
35 KiB
C++
1108 lines
35 KiB
C++
//==================================================================//
|
||
/*
|
||
AtomicParsley - util.cpp
|
||
|
||
AtomicParsley is GPL software; you can freely distribute,
|
||
redistribute, modify & use under the terms of the GNU General
|
||
Public License; either version 2 or its successor.
|
||
|
||
AtomicParsley is distributed under the GPL "AS IS", without
|
||
any warranty; without the implied warranty of merchantability
|
||
or fitness for either an expressed or implied particular purpose.
|
||
|
||
Please see the included GNU General Public License (GPL) for
|
||
your rights and further details; see the file COPYING. If you
|
||
cannot, write to the Free Software Foundation, 59 Temple Place
|
||
Suite 330, Boston, MA 02111-1307, USA. Or www.fsf.org
|
||
|
||
Copyright (C) 2006-2007 puck_lock
|
||
with contributions from others; see the CREDITS file
|
||
|
||
----------------------
|
||
Code Contributions by:
|
||
|
||
* SLarew - prevent writing past array in Convert_multibyteUTF16_to_wchar
|
||
bugfix
|
||
|
||
*/
|
||
//==================================================================//
|
||
|
||
#include "AtomicParsley.h"
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
// Filesytem routines //
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
/*----------------------
|
||
findFileSize
|
||
utf8_filepath - a pointer to a string (possibly utf8) of the full path to the
|
||
file
|
||
|
||
take an ascii/utf8 filepath (which if under a unicode enabled Win32 OS was
|
||
already converted from utf16le to utf8 at program start) and test if AP is
|
||
running on a unicode enabled Win32 OS. If it is and converted to utf8 (rather
|
||
than just stripped), convert the utf8 filepath to a utf16 (native-endian)
|
||
filepath & pass that to a wide stat. Or stat it with a utf8 filepath on Unixen &
|
||
win32 (stripped utf8).
|
||
----------------------*/
|
||
uint64_t findFileSize(const char *utf8_filepath) {
|
||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||
if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
|
||
wchar_t *utf16_filepath = Convert_multibyteUTF8_to_wchar(utf8_filepath);
|
||
|
||
struct _stati64 fileStats;
|
||
_wstati64(utf16_filepath, &fileStats);
|
||
|
||
free(utf16_filepath);
|
||
utf16_filepath = NULL;
|
||
return fileStats.st_size;
|
||
} else
|
||
#endif
|
||
{
|
||
struct stat fileStats;
|
||
stat(utf8_filepath, &fileStats);
|
||
return fileStats.st_size;
|
||
}
|
||
return 0; // won't ever get here.... unless this is win32, set to utf8 and the
|
||
// folder/file had unicode.... TODO (? use isUTF8() for high ascii?)
|
||
}
|
||
|
||
/*----------------------
|
||
APar_OpenFile
|
||
utf8_filepath - a pointer to a string (possibly utf8) of the full path to the
|
||
file file_flags - 3 bytes max for the flags to open the file with (read, write,
|
||
binary mode....)
|
||
|
||
take an ascii/utf8 filepath (which if under a unicode enabled Win32 OS was
|
||
already converted from utf16le to utf8 at program start) and test if AP is
|
||
running on a unicode enabled Win32 OS. If it is, convert the utf8 filepath to a
|
||
utf16 (native-endian) filepath & pass that to a wide fopen with the 8-bit file
|
||
flags changed to 16-bit file flags. Or open a utf8 file with vanilla fopen on
|
||
Unixen.
|
||
----------------------*/
|
||
FILE *APar_OpenFile(const char *utf8_filepath, const char *file_flags) {
|
||
FILE *aFile = NULL;
|
||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||
if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
|
||
wchar_t *Lfile_flags = (wchar_t *)malloc(sizeof(wchar_t) * 4);
|
||
memset(Lfile_flags, 0, sizeof(wchar_t) * 4);
|
||
mbstowcs(Lfile_flags, file_flags, strlen(file_flags));
|
||
|
||
wchar_t *utf16_filepath = Convert_multibyteUTF8_to_wchar(utf8_filepath);
|
||
|
||
aFile = _wfopen(utf16_filepath, Lfile_flags);
|
||
|
||
free(Lfile_flags);
|
||
Lfile_flags = NULL;
|
||
free(utf16_filepath);
|
||
utf16_filepath = NULL;
|
||
} else
|
||
#endif
|
||
{
|
||
aFile = fopen(utf8_filepath, file_flags);
|
||
}
|
||
|
||
if (!aFile) {
|
||
fprintf(stdout,
|
||
"AP error trying to fopen %s: %s\n",
|
||
utf8_filepath,
|
||
strerror(errno));
|
||
}
|
||
return aFile;
|
||
}
|
||
|
||
/*----------------------
|
||
openSomeFile
|
||
utf8_filepath - a pointer to a string (possibly utf8) of the full path to the
|
||
file open - flag to either open or close (function does both)
|
||
|
||
take an ascii/utf8 filepath and either open or close it; used for the main
|
||
ISO Base Media File; store the resulting FILE* in a global source_file
|
||
----------------------*/
|
||
FILE *APar_OpenISOBaseMediaFile(const char *utf8file, bool open) {
|
||
if (open && !file_opened) {
|
||
source_file = APar_OpenFile(utf8file, "rb");
|
||
if (source_file != nullptr) {
|
||
file_opened = true;
|
||
}
|
||
} else if (file_opened) {
|
||
fclose(source_file);
|
||
file_opened = false;
|
||
source_file = nullptr;
|
||
}
|
||
return source_file;
|
||
}
|
||
|
||
void TestFileExistence(const char *filePath, bool errorOut) {
|
||
FILE *a_file = NULL;
|
||
a_file = APar_OpenFile(filePath, "rb");
|
||
if ((a_file == NULL) && errorOut) {
|
||
fprintf(stderr,
|
||
"AtomicParsley error: can't open %s for reading: %s\n",
|
||
filePath,
|
||
strerror(errno));
|
||
exit(1);
|
||
} else {
|
||
if (a_file == NULL) {
|
||
fprintf(stderr,
|
||
"AtomicParsley warning: can't open %s for reading but continuing "
|
||
"anyway: %s\n",
|
||
filePath,
|
||
strerror(errno));
|
||
} else {
|
||
fclose(a_file);
|
||
}
|
||
}
|
||
}
|
||
|
||
#if defined(_WIN32)
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
// Win32 functions //
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
#ifndef HAVE_FSEEKO
|
||
|
||
int fseeko(FILE *stream, uint64_t pos, int whence) { // only using SEEK_SET here
|
||
if (whence == SEEK_SET) {
|
||
fpos_t fpos = pos;
|
||
return fsetpos(stream, &fpos);
|
||
} else {
|
||
return -1;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
#endif
|
||
|
||
/*----------------------
|
||
APar_OpenFileWin32
|
||
utf8_filepath - a pointer to a string (possibly utf8) of the full path
|
||
to the file
|
||
... - passed on to the CreateFile function
|
||
|
||
take an ascii/utf8 filepath (which if under a unicode enabled Win32 OS
|
||
was already converted from utf16le to utf8 at program start) and test if AP is
|
||
running on a unicode enabled Win32 OS. If it is, convert the utf8 filepath to a
|
||
utf16 (native-endian) filepath & pass that to a wide CreateFile with the 8-bit
|
||
file flags changed to 16-bit file flags, otherwise pass the utf8 filepath to an
|
||
ANSI CreateFile
|
||
----------------------*/
|
||
HANDLE APar_OpenFileWin32(const char *utf8_filepath,
|
||
DWORD dwDesiredAccess,
|
||
DWORD dwShareMode,
|
||
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
||
DWORD dwCreationDisposition,
|
||
DWORD dwFlagsAndAttributes,
|
||
HANDLE hTemplateFile) {
|
||
if (IsUnicodeWinOS() && UnicodeOutputStatus == WIN32_UTF16) {
|
||
HANDLE hFile = NULL;
|
||
wchar_t *utf16_filepath = Convert_multibyteUTF8_to_wchar(utf8_filepath);
|
||
hFile = CreateFileW(utf16_filepath,
|
||
dwDesiredAccess,
|
||
dwShareMode,
|
||
lpSecurityAttributes,
|
||
dwCreationDisposition,
|
||
dwFlagsAndAttributes,
|
||
hTemplateFile);
|
||
free(utf16_filepath);
|
||
return hFile;
|
||
} else {
|
||
return CreateFileA(utf8_filepath,
|
||
dwDesiredAccess,
|
||
dwShareMode,
|
||
lpSecurityAttributes,
|
||
dwCreationDisposition,
|
||
dwFlagsAndAttributes,
|
||
hTemplateFile);
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
// http://www.flipcode.com/articles/article_advstrings01.shtml
|
||
bool IsUnicodeWinOS() {
|
||
#if defined(_WIN32)
|
||
OSVERSIONINFOW os;
|
||
memset(&os, 0, sizeof(OSVERSIONINFOW));
|
||
os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
|
||
return (GetVersionExW(&os) != 0);
|
||
#else
|
||
return false;
|
||
#endif
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
// File reading routines //
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
const char *APar_strferror(FILE *f) {
|
||
if (feof(f) && ferror(f))
|
||
return "error and end of file";
|
||
else if (feof(f))
|
||
return "end of file";
|
||
else if (ferror(f))
|
||
return "error";
|
||
else
|
||
return "neither error nor end of file";
|
||
}
|
||
|
||
uint8_t APar_read8(FILE *ISObasemediafile, uint64_t pos) {
|
||
uint8_t a_byte = 0;
|
||
size_t size;
|
||
fseeko(ISObasemediafile, pos, SEEK_SET);
|
||
size = fread(&a_byte, 1, 1, ISObasemediafile);
|
||
if (size != 1) {
|
||
printf("%s read failed, expect 1, got %u: %s\n",
|
||
__FUNCTION__,
|
||
(unsigned int)size,
|
||
APar_strferror(ISObasemediafile));
|
||
exit(1);
|
||
}
|
||
return a_byte;
|
||
}
|
||
|
||
uint16_t APar_read16(char *buffer, FILE *ISObasemediafile, uint64_t pos) {
|
||
size_t size;
|
||
fseeko(ISObasemediafile, pos, SEEK_SET);
|
||
size = fread(buffer, 1, 2, ISObasemediafile);
|
||
if (size != 2) {
|
||
printf("%s read failed, expect 2, got %u: %s\n",
|
||
__FUNCTION__,
|
||
(unsigned int)size,
|
||
APar_strferror(ISObasemediafile));
|
||
exit(1);
|
||
}
|
||
return UInt16FromBigEndian(buffer);
|
||
}
|
||
|
||
uint32_t APar_read32(char *buffer, FILE *ISObasemediafile, uint64_t pos) {
|
||
size_t size;
|
||
fseeko(ISObasemediafile, pos, SEEK_SET);
|
||
size = fread(buffer, 1, 4, ISObasemediafile);
|
||
if (size != 4) {
|
||
printf("%s read failed, expect 4, got %u: %s\n",
|
||
__FUNCTION__,
|
||
(unsigned int)size,
|
||
APar_strferror(ISObasemediafile));
|
||
exit(1);
|
||
}
|
||
return UInt32FromBigEndian(buffer);
|
||
}
|
||
|
||
uint64_t APar_read64(char *buffer, FILE *ISObasemediafile, uint64_t pos) {
|
||
size_t size;
|
||
fseeko(ISObasemediafile, pos, SEEK_SET);
|
||
size = fread(buffer, 1, 8, ISObasemediafile);
|
||
if (size != 8) {
|
||
printf("%s read failed, expect 8, got %u: %s\n",
|
||
__FUNCTION__,
|
||
(unsigned int)size,
|
||
APar_strferror(ISObasemediafile));
|
||
exit(1);
|
||
}
|
||
return UInt64FromBigEndian(buffer);
|
||
}
|
||
|
||
void APar_readX_noseek(char *buffer, FILE *ISObasemediafile, uint32_t length) {
|
||
size_t size;
|
||
size = fread(buffer, 1, length, ISObasemediafile);
|
||
if (size != length) {
|
||
printf("%s read failed, expect %" PRIu32 ", got %" PRIu32 ": %s\n",
|
||
__FUNCTION__,
|
||
length,
|
||
(uint32_t)size,
|
||
APar_strferror(ISObasemediafile));
|
||
exit(1);
|
||
}
|
||
return;
|
||
}
|
||
|
||
void APar_readX(char *buffer,
|
||
FILE *ISObasemediafile,
|
||
uint64_t pos,
|
||
uint32_t length) {
|
||
size_t size;
|
||
fseeko(ISObasemediafile, pos, SEEK_SET);
|
||
size = fread(buffer, 1, length, ISObasemediafile);
|
||
if (size != length) {
|
||
printf("%s read failed, expect %" PRIu32 ", got %" PRIu32 ": %s\n",
|
||
__FUNCTION__,
|
||
length,
|
||
(uint32_t)size,
|
||
APar_strferror(ISObasemediafile));
|
||
exit(1);
|
||
}
|
||
return;
|
||
}
|
||
|
||
uint32_t
|
||
APar_ReadFile(char *destination_buffer, FILE *a_file, uint32_t bytes_to_read) {
|
||
uint32_t bytes_read = 0;
|
||
if (destination_buffer != NULL) {
|
||
fseeko(a_file, 0, SEEK_SET); // not that 2gb support is required - malloc
|
||
// would probably have a few issues
|
||
bytes_read = fread(destination_buffer, 1, bytes_to_read, a_file);
|
||
file_size += bytes_read; // accommodate huge files embedded within small
|
||
// files for APar_Validate
|
||
}
|
||
return bytes_read;
|
||
}
|
||
|
||
uint32_t APar_FindValueInAtom(char *uint32_buffer,
|
||
FILE *ISObasemediafile,
|
||
short an_atom,
|
||
uint64_t start_position,
|
||
uint32_t eval_number) {
|
||
uint64_t current_pos = start_position;
|
||
memset(uint32_buffer, 0, 5);
|
||
while (current_pos <= parsedAtoms[an_atom].AtomicLength) {
|
||
current_pos++;
|
||
if (eval_number > 65535) {
|
||
// current_pos +=4;
|
||
if (APar_read32(uint32_buffer,
|
||
ISObasemediafile,
|
||
parsedAtoms[an_atom].AtomicStart + current_pos) ==
|
||
eval_number) {
|
||
break;
|
||
}
|
||
} else {
|
||
// current_pos +=2;
|
||
if (APar_read16(uint32_buffer,
|
||
ISObasemediafile,
|
||
parsedAtoms[an_atom].AtomicStart + current_pos) ==
|
||
(uint16_t)eval_number) {
|
||
break;
|
||
}
|
||
}
|
||
if (current_pos >= parsedAtoms[an_atom].AtomicLength) {
|
||
current_pos = 0;
|
||
break;
|
||
}
|
||
}
|
||
return (uint32_t)current_pos;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
// Language specifics //
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void APar_UnpackLanguage(unsigned char lang_code[], uint16_t packed_language) {
|
||
lang_code[3] = 0;
|
||
lang_code[2] = (packed_language & 0x1F) + 0x60;
|
||
lang_code[1] = ((packed_language >> 5) & 0x1F) + 0x60;
|
||
lang_code[0] = ((packed_language >> 10) & 0x1F) + 0x60;
|
||
return;
|
||
}
|
||
|
||
uint16_t PackLanguage(
|
||
const char *language_code,
|
||
uint8_t lang_offset) { //?? is there a problem here? und does't work
|
||
// http://www.w3.org/WAI/ER/IG/ert/iso639.htm
|
||
// I think Apple's 3gp asses decoder is a little off. First, it doesn't
|
||
// support a lot of those 3 letter language codes above on that page. for
|
||
// example 'zul' blocks *all* metadata from showing up. 'fre' is a no-no, but
|
||
// 'fra' is fine. then, the spec calls for all strings to be null terminated.
|
||
// So then why does a '<27> 2005' (with a NULL at the end) show up as '<27> 2005' in
|
||
// 'pol', but '<27> 2005 ?' in 'fas' Farsi? Must be Apple's implementation,
|
||
// because the files are identical except for the uint16_t lang setting.
|
||
|
||
uint16_t packed_language = 0;
|
||
|
||
// fprintf(stdout, "%i, %i, %i\n", language_code[0+lang_offset],
|
||
// language_code[1+lang_offset], language_code[2+lang_offset]);
|
||
|
||
if (language_code[0 + lang_offset] < 97 ||
|
||
language_code[0 + lang_offset] > 122 ||
|
||
language_code[1 + lang_offset] < 97 ||
|
||
language_code[1 + lang_offset] > 122 ||
|
||
language_code[2 + lang_offset] < 97 ||
|
||
language_code[2 + lang_offset] > 122) {
|
||
|
||
return packed_language;
|
||
}
|
||
|
||
packed_language = (((language_code[0 + lang_offset] - 0x60) & 0x1F) << 10) |
|
||
(((language_code[1 + lang_offset] - 0x60) & 0x1F) << 5) |
|
||
((language_code[2 + lang_offset] - 0x60) & 0x1F);
|
||
return packed_language;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
// platform specifics //
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
#ifndef HAVE_STRSEP
|
||
// use glibc's strsep only on windows when cygwin & libc are undefined;
|
||
// otherwise the internal strsep will be used This marks the point where a
|
||
// ./configure & makefile combo would make this easier
|
||
|
||
/* Copyright (C) 1992, 93, 96, 97, 98, 99, 2004 Free Software Foundation, Inc.
|
||
This strsep function is part of the GNU C Library - v2.3.5; LGPL.
|
||
*/
|
||
|
||
char *strsep(char **stringp, const char *delim) {
|
||
char *begin, *end;
|
||
|
||
begin = *stringp;
|
||
if (begin == NULL)
|
||
return NULL;
|
||
|
||
// A frequent case is when the delimiter string contains only one character.
|
||
// Here we don't need to call the expensive `strpbrk' function and instead
|
||
// work using `strchr'.
|
||
if (delim[0] == '\0' || delim[1] == '\0') {
|
||
char ch = delim[0];
|
||
|
||
if (ch == '\0')
|
||
end = NULL;
|
||
else {
|
||
if (*begin == ch)
|
||
end = begin;
|
||
else if (*begin == '\0')
|
||
end = NULL;
|
||
else
|
||
end = strchr(begin + 1, ch);
|
||
}
|
||
} else
|
||
|
||
end = strpbrk(begin, delim); // Find the end of the token.
|
||
|
||
if (end) {
|
||
*end++ = '\0'; // Terminate the token and set *STRINGP past NUL character.
|
||
*stringp = end;
|
||
} else
|
||
*stringp = NULL; // No more delimiters; this is the last token.
|
||
|
||
return begin;
|
||
}
|
||
#endif
|
||
|
||
void determine_MonthDay(int literal_day, int &month, int &day) {
|
||
if (literal_day <= 31) {
|
||
month = 1;
|
||
day = literal_day;
|
||
|
||
} else if (literal_day <= 59) {
|
||
month = 2;
|
||
day = literal_day - 31;
|
||
|
||
} else if (literal_day <= 90) {
|
||
month = 3;
|
||
day = literal_day - 59;
|
||
|
||
} else if (literal_day <= 120) {
|
||
month = 4;
|
||
day = literal_day - 90;
|
||
|
||
} else if (literal_day <= 151) {
|
||
month = 5;
|
||
day = literal_day - 120;
|
||
|
||
} else if (literal_day <= 181) {
|
||
month = 6;
|
||
day = literal_day - 151;
|
||
|
||
} else if (literal_day <= 212) {
|
||
month = 7;
|
||
day = literal_day - 181;
|
||
|
||
} else if (literal_day <= 243) {
|
||
month = 8;
|
||
day = literal_day - 212;
|
||
|
||
} else if (literal_day <= 273) {
|
||
month = 9;
|
||
day = literal_day - 243;
|
||
|
||
} else if (literal_day <= 304) {
|
||
month = 10;
|
||
day = literal_day - 273;
|
||
|
||
} else if (literal_day <= 334) {
|
||
month = 11;
|
||
day = literal_day - 304;
|
||
|
||
} else if (literal_day <= 365) {
|
||
month = 12;
|
||
day = literal_day - 334;
|
||
}
|
||
return;
|
||
}
|
||
|
||
char *APar_gmtime64(uint64_t total_secs, char *utc_time) {
|
||
// this will probably be off between Jan 1 & Feb 28 on a leap year by a
|
||
// day.... I'll somehow cope & deal.
|
||
struct tm timeinfo = {0, 0, 0, 0, 0};
|
||
|
||
int offset_year =
|
||
(int)(total_secs / 31536000); // 60 * 60 * 24 * 365 (ordinary year in
|
||
// seconds; doesn't account for leap year)
|
||
int literal_year = 1904 + offset_year;
|
||
int literal_days_into_year = ((total_secs % 31536000) / 86400) -
|
||
(offset_year / 4); // accounts for the leap year
|
||
|
||
uint32_t literal_seconds_into_day = total_secs % 86400;
|
||
|
||
int month = 0;
|
||
int days = 0;
|
||
|
||
determine_MonthDay(literal_days_into_year, month, days);
|
||
|
||
if (literal_days_into_year < 0) {
|
||
literal_year -= 1;
|
||
literal_days_into_year = 31 + literal_days_into_year;
|
||
month = 12;
|
||
days = literal_days_into_year;
|
||
}
|
||
|
||
int hours = literal_seconds_into_day / 3600;
|
||
|
||
timeinfo.tm_year = literal_year - 1900;
|
||
timeinfo.tm_yday = literal_days_into_year;
|
||
timeinfo.tm_mon = month - 1;
|
||
timeinfo.tm_mday = days;
|
||
timeinfo.tm_wday = (((total_secs / 86400) - (offset_year / 4)) - 5) % 7;
|
||
|
||
timeinfo.tm_hour = hours;
|
||
timeinfo.tm_min = (literal_seconds_into_day - (hours * 3600)) / 60;
|
||
timeinfo.tm_sec = (int)(literal_seconds_into_day % 60);
|
||
|
||
strftime(utc_time, 50, "%a %b %d %H:%M:%S %Y", &timeinfo);
|
||
return utc_time;
|
||
}
|
||
|
||
/*----------------------
|
||
ExtractUTC
|
||
total_secs - the time in seconds (from Jan 1, 1904)
|
||
|
||
Convert the seconds to a calendar date with seconds.
|
||
----------------------*/
|
||
char *APar_extract_UTC(uint64_t total_secs) {
|
||
// 2082844800 seconds between 01/01/1904 & 01/01/1970
|
||
// 2,081,376,000 (60 seconds * 60 minutes * 24 hours * 365 days * 66 years)
|
||
// + 1,468,800 (60 * 60 * 24 * 17 leap days in 01/01/1904 to 01/01/1970
|
||
// duration)
|
||
//= 2,082,844,800
|
||
static char utc_time[50];
|
||
memset(utc_time, 0, 50);
|
||
|
||
if (total_secs > MAXTIME_32) {
|
||
return APar_gmtime64(total_secs, utc_time);
|
||
} else {
|
||
if (total_secs < 2082844800) {
|
||
return APar_gmtime64(total_secs, utc_time); // less than Unix epoch
|
||
} else {
|
||
total_secs -= 2082844800;
|
||
time_t reduced_seconds = (time_t)total_secs;
|
||
strftime(
|
||
*&utc_time, 50, "%a %b %d %H:%M:%S %Y", gmtime(&reduced_seconds));
|
||
return *&utc_time;
|
||
}
|
||
}
|
||
return *&utc_time;
|
||
}
|
||
|
||
uint32_t APar_get_mpeg4_time() {
|
||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||
FILETIME file_time;
|
||
uint64_t wintime = 0;
|
||
GetSystemTimeAsFileTime(&file_time);
|
||
wintime =
|
||
(((uint64_t)file_time.dwHighDateTime << 32) | file_time.dwLowDateTime) /
|
||
10000000;
|
||
wintime -= 9561628800ULL;
|
||
return (uint32_t)wintime;
|
||
|
||
#else
|
||
uint32_t current_time_in_seconds = 0;
|
||
struct timeval tv;
|
||
gettimeofday(&tv, NULL);
|
||
current_time_in_seconds = tv.tv_sec;
|
||
return current_time_in_seconds + 2082844800;
|
||
|
||
#endif
|
||
return 0;
|
||
}
|
||
|
||
/*----------------------
|
||
APar_StandardTime
|
||
formed_time - the destination string
|
||
|
||
Print the ISO 8601 Coordinated Universal Time (UTC) timestamp (in
|
||
YYYY-MM-DDTHH:MM:SSZ form)
|
||
----------------------*/
|
||
void APar_StandardTime(char *&formed_time) {
|
||
time_t rawtime;
|
||
struct tm *timeinfo;
|
||
|
||
time(&rawtime);
|
||
timeinfo = gmtime(&rawtime);
|
||
strftime(formed_time,
|
||
100,
|
||
"%Y-%m-%dT%H:%M:%SZ",
|
||
timeinfo); // that hanging Z is there; denotes the UTC
|
||
|
||
return;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
// strings //
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
wchar_t *
|
||
Convert_multibyteUTF16_to_wchar(char *input_unicode,
|
||
size_t glyph_length,
|
||
bool skip_BOM) { // TODO: is this like wcstombs?
|
||
int BOM_mark_bytes = 0;
|
||
if (skip_BOM) {
|
||
BOM_mark_bytes = 2;
|
||
}
|
||
|
||
wchar_t *utf16_data = (wchar_t *)malloc(
|
||
sizeof(wchar_t) *
|
||
(glyph_length + 1)); // just to be sure there will be a trailing NULL
|
||
wmemset(utf16_data, 0, glyph_length + 1);
|
||
|
||
for (size_t i = 0; i < glyph_length; i++) {
|
||
#if defined(__ppc__) || defined(__ppc64__)
|
||
utf16_data[i] = (input_unicode[2 * i + BOM_mark_bytes] & 0x00ff) << 8 |
|
||
(input_unicode[2 * i + 1 + BOM_mark_bytes])
|
||
<< 0; //+2 & +3 to skip over the BOM
|
||
#else
|
||
utf16_data[i] = (input_unicode[2 * i + BOM_mark_bytes] << 8) |
|
||
((input_unicode[2 * i + 1 + BOM_mark_bytes]) & 0x00ff)
|
||
<< 0; //+2 & +3 to skip over the BOM
|
||
#endif
|
||
}
|
||
return utf16_data;
|
||
}
|
||
|
||
unsigned char *Convert_multibyteUTF16_to_UTF8(char *input_utf16,
|
||
size_t glyph_length,
|
||
size_t byte_count) {
|
||
unsigned char *utf8_data =
|
||
(unsigned char *)malloc(sizeof(unsigned char) * glyph_length);
|
||
memset(utf8_data, 0, glyph_length);
|
||
|
||
UTF16BEToUTF8(
|
||
utf8_data, glyph_length, (unsigned char *)input_utf16 + 2, byte_count);
|
||
return utf8_data;
|
||
}
|
||
|
||
wchar_t *Convert_multibyteUTF8_to_wchar(
|
||
const char *input_utf8) { // TODO: is this like mbstowcs?
|
||
wchar_t *return_val = NULL;
|
||
size_t string_length = strlen(input_utf8) + 1; // account for terminating NULL
|
||
size_t char_glyphs = mbstowcs(
|
||
NULL,
|
||
input_utf8,
|
||
string_length); // passing NULL pre-calculates the size of wchar_t needed
|
||
|
||
unsigned char *utf16_conversion =
|
||
(unsigned char *)malloc(sizeof(unsigned char) * string_length * 2);
|
||
memset(utf16_conversion, 0, string_length * 2);
|
||
|
||
int utf_16_glyphs = UTF8ToUTF16BE(utf16_conversion,
|
||
char_glyphs * 2,
|
||
(unsigned char *)input_utf8,
|
||
string_length) /
|
||
2; // returned value is in bytes
|
||
return_val = Convert_multibyteUTF16_to_wchar(
|
||
(char *)utf16_conversion, (size_t)utf_16_glyphs, false);
|
||
free(utf16_conversion);
|
||
utf16_conversion = NULL;
|
||
return (return_val);
|
||
}
|
||
|
||
// these flags from id3v2 2.4
|
||
// 0x00 = ISO-8859-1 & terminate with 0x00.
|
||
// 0x01 = UTF-16 with BOM. All frames have same encoding & terminate with
|
||
// 0x0000. 0x02 = UTF-16BE without BOM & terminate with 0x0000. 0x03 = UTF-8 &
|
||
// terminated with 0x00. buffer can hold either ut8 or utf16 carried on 8-bit
|
||
// char which requires a cast
|
||
/*----------------------
|
||
findstringNULLterm
|
||
in_string - pointer to location of a string (can be either 8859-1, utf8 or
|
||
utf16be/utf16be needing a cast to wchar) encodingFlag - used to denote the
|
||
encoding of instring (derived from id3v2 2.4 encoding flags) max_len - the
|
||
length of given string - there may be no NULL terminaiton, in which case it will
|
||
only count to max_len
|
||
|
||
Either find the NULL if it exists and return how many bytes into in_string
|
||
that NULL exists, or it won't find a NULL and return max_len
|
||
----------------------*/
|
||
uint32_t
|
||
findstringNULLterm(char *in_string, uint8_t encodingFlag, uint32_t max_len) {
|
||
uint32_t byte_count = 0;
|
||
|
||
if (encodingFlag == 0x00 || encodingFlag == 0x03) {
|
||
char *bufptr = in_string;
|
||
while (bufptr <= in_string + max_len) {
|
||
if (*bufptr == 0x00) {
|
||
break;
|
||
}
|
||
bufptr++;
|
||
byte_count++;
|
||
}
|
||
} else if ((encodingFlag == 0x01 || encodingFlag == 0x02) && max_len >= 2) {
|
||
short wbufptr;
|
||
while (byte_count <= max_len) {
|
||
wbufptr =
|
||
(*(in_string + byte_count) << 8) | *(in_string + byte_count + 1);
|
||
if (wbufptr == 0x0000) {
|
||
break;
|
||
}
|
||
byte_count += 2;
|
||
}
|
||
}
|
||
if (byte_count > max_len)
|
||
return max_len;
|
||
return byte_count;
|
||
}
|
||
|
||
uint32_t skipNULLterm(char *in_string, uint8_t encodingFlag, uint32_t max_len) {
|
||
uint32_t byte_count = 0;
|
||
|
||
if (encodingFlag == 0x00 || encodingFlag == 0x03) {
|
||
char *bufptr = in_string;
|
||
while (bufptr <= in_string + max_len) {
|
||
if (*bufptr == 0x00) {
|
||
byte_count++;
|
||
break;
|
||
}
|
||
bufptr++;
|
||
}
|
||
} else if ((encodingFlag == 0x01 || encodingFlag == 0x02) && max_len >= 2) {
|
||
short wbufptr;
|
||
while (byte_count <= max_len) {
|
||
wbufptr =
|
||
(*(in_string + byte_count) << 8) | *(in_string + byte_count + 1);
|
||
if (wbufptr == 0x0000) {
|
||
byte_count += 2;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return byte_count;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
// generics //
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
uint16_t UInt16FromBigEndian(const char *string) {
|
||
#if defined(__ppc__) || defined(__ppc64__)
|
||
uint16_t test;
|
||
memcpy(&test, string, 2);
|
||
return test;
|
||
#else
|
||
return (((string[0] & 0xff) << 8) | (string[1] & 0xff) << 0);
|
||
#endif
|
||
}
|
||
|
||
uint32_t UInt32FromBigEndian(const char *string) {
|
||
#if defined(__ppc__) || defined(__ppc64__)
|
||
uint32_t test;
|
||
memcpy(&test, string, 4);
|
||
return test;
|
||
#else
|
||
return (((string[0] & 0xff) << 24) | ((string[1] & 0xff) << 16) |
|
||
((string[2] & 0xff) << 8) | (string[3] & 0xff) << 0);
|
||
#endif
|
||
}
|
||
|
||
uint64_t UInt64FromBigEndian(const char *string) {
|
||
#if defined(__ppc__) || defined(__ppc64__)
|
||
uint64_t test;
|
||
memcpy(&test, string, 8);
|
||
return test;
|
||
#else
|
||
return (uint64_t)(string[0] & 0xff) << 54 |
|
||
(uint64_t)(string[1] & 0xff) << 48 |
|
||
(uint64_t)(string[2] & 0xff) << 40 |
|
||
(uint64_t)(string[3] & 0xff) << 32 |
|
||
(uint64_t)(string[4] & 0xff) << 24 |
|
||
(uint64_t)(string[5] & 0xff) << 16 |
|
||
(uint64_t)(string[6] & 0xff) << 8 | (uint64_t)(string[7] & 0xff) << 0;
|
||
#endif
|
||
}
|
||
|
||
void UInt16_TO_String2(uint16_t snum, char *data) {
|
||
data[0] = (snum >> 8) & 0xff;
|
||
data[1] = (snum >> 0) & 0xff;
|
||
return;
|
||
}
|
||
|
||
void UInt32_TO_String4(uint32_t lnum, char *data) {
|
||
data[0] = (lnum >> 24) & 0xff;
|
||
data[1] = (lnum >> 16) & 0xff;
|
||
data[2] = (lnum >> 8) & 0xff;
|
||
data[3] = (lnum >> 0) & 0xff;
|
||
return;
|
||
}
|
||
|
||
void UInt64_TO_String8(uint64_t ullnum, char *data) {
|
||
data[0] = (ullnum >> 56) & 0xff;
|
||
data[1] = (ullnum >> 48) & 0xff;
|
||
data[2] = (ullnum >> 40) & 0xff;
|
||
data[3] = (ullnum >> 32) & 0xff;
|
||
data[4] = (ullnum >> 24) & 0xff;
|
||
data[5] = (ullnum >> 16) & 0xff;
|
||
data[6] = (ullnum >> 8) & 0xff;
|
||
data[7] = (ullnum >> 0) & 0xff;
|
||
return;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
// 3gp asset support (for 'loci') //
|
||
///////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
uint32_t float_to_16x16bit_fixed_point(double floating_val) {
|
||
uint32_t fixedpoint_16bit = 0;
|
||
int16_t long_integer = (int16_t)floating_val;
|
||
// to get a fixed 16-bit decimal, work on the decimal part along; multiply by
|
||
// (2^8 * 2) which moves the decimal over 16 bits to create our int16_t now
|
||
// while the degrees can be negative (requiring a int16_6), the decimal
|
||
// portion is always positive (and thus requiring a uint16_t)
|
||
uint16_t long_decimal =
|
||
(int16_t)((floating_val - long_integer) * (double)(65536));
|
||
fixedpoint_16bit =
|
||
long_integer * 65536 +
|
||
long_decimal; // same as bitshifting, less headache doing it
|
||
return fixedpoint_16bit;
|
||
}
|
||
|
||
double fixed_point_16x16bit_to_double(uint32_t fixed_point) {
|
||
double return_val = 0.0;
|
||
int16_t long_integer = fixed_point / 65536;
|
||
uint16_t long_decimal = fixed_point - (long_integer * 65536);
|
||
return_val = long_integer + ((double)long_decimal / 65536);
|
||
|
||
if (return_val < 0.0) {
|
||
return_val -= 1.0;
|
||
}
|
||
|
||
return return_val;
|
||
}
|
||
|
||
uint32_t widechar_len(char *instring, uint32_t _bytes_) {
|
||
uint32_t wstring_len = 0;
|
||
for (uint32_t i = 0; i <= _bytes_ / 2; i++) {
|
||
if (instring[0] == 0 && instring[1] == 0) {
|
||
break;
|
||
} else {
|
||
instring += 2;
|
||
wstring_len++;
|
||
}
|
||
}
|
||
return wstring_len;
|
||
}
|
||
|
||
bool APar_assert(bool expression,
|
||
int error_msg,
|
||
const char *supplemental_info) {
|
||
bool force_break = true;
|
||
if (!expression) {
|
||
force_break = false;
|
||
switch (error_msg) {
|
||
case 1: { // trying to set an iTunes-style metadata tag on an
|
||
// 3GP/MobileMPEG-4
|
||
fprintf(stdout,
|
||
"AP warning:\n\tSetting the %s tag is for ordinary MPEG-4 "
|
||
"files.\n\tIt is not supported on 3gp/amc files.\nSkipping\n",
|
||
supplemental_info);
|
||
break;
|
||
}
|
||
|
||
case 2: { // trying to set a 3gp asset on an mpeg-4 file with the improper
|
||
// brand
|
||
fprintf(stdout,
|
||
"AP warning:\n\tSetting the %s asset is only available on 3GPP "
|
||
"files branded 3gp6 or later.\nSkipping\n",
|
||
supplemental_info);
|
||
break;
|
||
}
|
||
|
||
case 3: { // trying to set 'meta' on a file without a iso2 or mp42
|
||
// compatible brand.
|
||
fprintf(stdout,
|
||
"AtomicParsley warning: ID3 tags requires a v2 "
|
||
"compatible file, which was not found.\nSkipping.\n");
|
||
break;
|
||
}
|
||
case 4: { // trying to set a 3gp album asset on an early 3gp file that only
|
||
// came into being with 3gp6
|
||
fprintf(stdout, "Major brand of given file: %s\n", supplemental_info);
|
||
break;
|
||
}
|
||
case 5: { // trying to set metadata on track 33 when there are only 3 tracks
|
||
fprintf(stdout,
|
||
"AP warning: skipping non-existing track number setting user "
|
||
"data atom: %s.\n",
|
||
supplemental_info);
|
||
break;
|
||
}
|
||
case 6: { // trying to set id3 metadata on track 33 when there are only 3
|
||
// tracks
|
||
fprintf(stdout,
|
||
"AP error: skipping non-existing track number setting frame %s "
|
||
"for ID32 atom.\n",
|
||
supplemental_info);
|
||
break;
|
||
}
|
||
case 7: { // trying to set id3 metadata on track 33 when there are only 3
|
||
// tracks
|
||
fprintf(stdout,
|
||
"AP warning: the 'meta' atom is being hangled by a %s handler.\n "
|
||
" Remove the 'meta' atom and its contents and try again.\n",
|
||
supplemental_info);
|
||
break;
|
||
}
|
||
case 8: { // trying to create an ID32 atom when there is a primary item atom
|
||
// present signaling referenced data (local or external)
|
||
fprintf(stdout,
|
||
"AP warning: unsupported external or referenced items were "
|
||
"detected. Skipping this frame: %s\n",
|
||
supplemental_info);
|
||
break;
|
||
}
|
||
case 9: { // trying to eliminate an id3 frame that doesn't exist
|
||
fprintf(stdout,
|
||
"AP warning: id3 frame %s cannot be deleted because it does not "
|
||
"exist.\n",
|
||
supplemental_info);
|
||
break;
|
||
}
|
||
case 10: { // trying to eliminate an id3 frame that doesn't exist
|
||
fprintf(stdout,
|
||
"AP warning: skipping setting unknown %s frame\n",
|
||
supplemental_info);
|
||
break;
|
||
}
|
||
case 11: { // insuffient memory to malloc an id3 field (probably picture or
|
||
// encapuslated object)
|
||
fprintf(stdout,
|
||
"AP error: memory was not alloctated for frame %s. Exiting.\n",
|
||
supplemental_info);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return force_break;
|
||
}
|
||
|
||
/* http://wwwmaths.anu.edu.au/~brent/random.html */
|
||
/* xorgens.c version 3.04, R. P. Brent, 20060628. */
|
||
|
||
/* For type definitions see xorgens.h */
|
||
|
||
typedef unsigned long xorgenUINT;
|
||
|
||
unsigned long xor4096i() {
|
||
/* 32-bit or 64-bit integer random number generator
|
||
with period at least 2**4096-1.
|
||
|
||
It is assumed that "xorgenUINT" is a 32-bit or 64-bit integer
|
||
(see typedef statements in xorgens.h).
|
||
|
||
xor4096i should be called exactly once with nonzero seed, and
|
||
thereafter with zero seed.
|
||
|
||
One random number uniformly distributed in [0..2**wlen) is returned,
|
||
where wlen = 8*sizeof(xorgenUINT) = 32 or 64.
|
||
|
||
R. P. Brent, 20060628.
|
||
*/
|
||
|
||
/* UINT64 is TRUE if 64-bit xorgenUINT,
|
||
UINT32 is TRUE otherwise (assumed to be 32-bit xorgenUINT). */
|
||
|
||
#define UINT64 (sizeof(xorgenUINT) >> 3)
|
||
#define UINT32 (1 - UINT64)
|
||
|
||
#define wlen (64 * UINT64 + 32 * UINT32)
|
||
#define r (64 * UINT64 + 128 * UINT32)
|
||
#define s (53 * UINT64 + 95 * UINT32)
|
||
#define a (33 * UINT64 + 17 * UINT32)
|
||
#define b (26 * UINT64 + 12 * UINT32)
|
||
#define c (27 * UINT64 + 13 * UINT32)
|
||
#define d (29 * UINT64 + 15 * UINT32)
|
||
#define ws (27 * UINT64 + 16 * UINT32)
|
||
|
||
xorgenUINT seed = 0;
|
||
|
||
static xorgenUINT w, weyl, zero = 0, x[r];
|
||
xorgenUINT t, v;
|
||
static int i = -1; /* i < 0 indicates first call */
|
||
int k;
|
||
|
||
if (i < 0) {
|
||
#if defined HAVE_SRANDDEV
|
||
sranddev();
|
||
#else
|
||
srand((int)time(NULL));
|
||
#endif
|
||
double doubleseed = ((double)rand() / ((double)(RAND_MAX) + (double)(1)));
|
||
seed = (xorgenUINT)(doubleseed * rand());
|
||
}
|
||
|
||
if ((i < 0) || (seed != zero)) { /* Initialisation necessary */
|
||
|
||
/* weyl = odd approximation to 2**wlen*(sqrt(5)-1)/2. */
|
||
|
||
if (UINT32)
|
||
weyl = 0x61c88647;
|
||
else
|
||
weyl = ((((xorgenUINT)0x61c88646) << 16) << 16) + (xorgenUINT)0x80b583eb;
|
||
|
||
v = (seed != zero) ? seed : ~seed; /* v must be nonzero */
|
||
|
||
for (k = wlen; k > 0; k--) { /* Avoid correlations for close seeds */
|
||
v ^= v << 10;
|
||
v ^= v >> 15; /* Recurrence has period 2**wlen-1 */
|
||
v ^= v << 4;
|
||
v ^= v >> 13; /* for wlen = 32 or 64 */
|
||
}
|
||
for (w = v, k = 0; (xorgenUINT)k < r; k++) { /* Initialise circular array */
|
||
v ^= v << 10;
|
||
v ^= v >> 15;
|
||
v ^= v << 4;
|
||
v ^= v >> 13;
|
||
x[k] = v + (w += weyl);
|
||
}
|
||
for (i = r - 1, k = 4 * r; k > 0; k--) { /* Discard first 4*r results */
|
||
t = x[i = (i + 1) & (r - 1)];
|
||
t ^= t << a;
|
||
t ^= t >> b;
|
||
v = x[(i + (r - s)) & (r - 1)];
|
||
v ^= v << c;
|
||
v ^= v >> d;
|
||
x[i] = t ^ v;
|
||
}
|
||
}
|
||
|
||
/* Apart from initialisation (above), this is the generator */
|
||
|
||
t = x[i = (i + 1) & (r - 1)]; /* Assumes that r is a power of two */
|
||
v = x[(i + (r - s)) & (r - 1)]; /* Index is (i-s) mod r */
|
||
t ^= t << a;
|
||
t ^= t >> b; /* (I + L^a)(I + R^b) */
|
||
v ^= v << c;
|
||
v ^= v >> d; /* (I + L^c)(I + R^d) */
|
||
x[i] = (v ^= t); /* Update circular array */
|
||
w += weyl; /* Update Weyl generator */
|
||
return (v + (w ^ (w >> ws))); /* Return combination */
|
||
|
||
#undef UINT64
|
||
#undef UINT32
|
||
#undef wlen
|
||
#undef r
|
||
#undef s
|
||
#undef a
|
||
#undef b
|
||
#undef c
|
||
#undef d
|
||
#undef ws
|
||
}
|