atomicparsley/src/metalist.cpp
2020-12-14 13:35:27 +08:00

2164 lines
78 KiB
C++
Raw Blame History

//==================================================================//
/*
AtomicParsley - metalist.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
*/
//==================================================================//
#include "AtomicParsley.h"
bool BOM_printed = false;
uint32_t APar_ProvideTallyForAtom(const char *atom_name) {
uint32_t tally_for_atom = 0;
short iter = parsedAtoms[0].NextAtomNumber;
while (true) {
if (memcmp(parsedAtoms[iter].AtomicName, atom_name, 4) == 0) {
if (parsedAtoms[iter].AtomicLength == 0) {
tally_for_atom += file_size - parsedAtoms[iter].AtomicStart;
} else if (parsedAtoms[iter].AtomicLength == 1) {
tally_for_atom += parsedAtoms[iter].AtomicLengthExtended;
} else {
tally_for_atom += parsedAtoms[iter].AtomicLength;
}
}
if (iter == 0) {
break;
} else {
iter = parsedAtoms[iter].NextAtomNumber;
}
}
return tally_for_atom;
}
void printBOM() {
if (BOM_printed)
return;
#if defined(_WIN32) && !defined(__CYGWIN__)
if (UnicodeOutputStatus == WIN32_UTF16) {
APar_unicode_win32Printout(L"\xEF\xBB\xBF", "\xEF\xBB\xBF");
}
#else
fprintf(stdout, "\xEF\xBB\xBF"); // Default to output of a UTF-8 BOM
#endif
BOM_printed = true;
return;
}
#if defined(_WIN32) && !defined(__CYGWIN__)
void APar_unicode_win32Printout(
wchar_t *unicode_out,
char *
utf8_out) { // based on
// http://blogs.msdn.com/junfeng/archive/2004/02/25/79621.aspx
// its possible that this isn't even available on windows95
DWORD dwBytesWritten;
DWORD fdwMode;
HANDLE outHandle = GetStdHandle(STD_OUTPUT_HANDLE);
// ThreadLocale adjustment, resource loading, etc. is skipped
if ((GetFileType(outHandle) & FILE_TYPE_CHAR) &&
GetConsoleMode(outHandle, &fdwMode)) {
if (wcsncmp(unicode_out, L"\xEF\xBB\xBF", 3) !=
0) { // skip BOM when writing directly to the console
WriteConsoleW(
outHandle, unicode_out, wcslen(unicode_out), &dwBytesWritten, 0);
}
} else {
// writing out to a file. Everything will be written out in utf8 to the
// file.
fprintf(stdout, "%s", utf8_out);
}
return;
}
#endif
void APar_fprintf_UTF8_data(const char *utf8_encoded_data) {
#if defined(_WIN32) && !defined(__CYGWIN__)
if (GetVersion() & 0x80000000 || UnicodeOutputStatus == UNIVERSAL_UTF8) {
fprintf(stdout,
"%s",
utf8_encoded_data); // just printout the raw utf8 bytes (not
// characters) under pre-NT windows
} else {
wchar_t *utf16_data = Convert_multibyteUTF8_to_wchar(utf8_encoded_data);
fflush(stdout);
APar_unicode_win32Printout(utf16_data, (char *)utf8_encoded_data);
fflush(stdout);
free(utf16_data);
utf16_data = NULL;
}
#else
fprintf(stdout, "%s", utf8_encoded_data);
#endif
return;
}
void APar_Mark_UserData_area(uint8_t track_num,
short userdata_atom,
bool quantum_listing) {
if (quantum_listing && track_num > 0) {
fprintf(stdout,
"User data; level: track=%u; atom \"%s\" ",
track_num,
parsedAtoms[userdata_atom].AtomicName);
} else if (quantum_listing && track_num == 0) {
fprintf(stdout,
"User data; level: movie; atom \"%s\" ",
parsedAtoms[userdata_atom].AtomicName);
} else {
fprintf(stdout, "User data \"%s\" ", parsedAtoms[userdata_atom].AtomicName);
}
return;
}
// the difference between APar_PrintUnicodeAssest above and
// APar_SimplePrintUnicodeAssest below is: APar_PrintUnicodeAssest contains the
// entire contents of the atom, NULL bytes and all APar_SimplePrintUnicodeAssest
// contains a purely unicode string (either utf8 or utf16 with BOM) and slight
// output formatting differences
void APar_SimplePrintUnicodeAssest(char *unicode_string,
int asset_length,
bool print_encoding) { // 3gp files
if (strncmp(unicode_string, "\xFE\xFF", 2) == 0) { // utf16
if (print_encoding) {
fprintf(stdout, " (utf16): ");
}
unsigned char *utf8_data = Convert_multibyteUTF16_to_UTF8(
unicode_string, asset_length * 6, asset_length);
#if defined(_WIN32) && !defined(__CYGWIN__)
if (GetVersion() & 0x80000000 ||
UnicodeOutputStatus ==
UNIVERSAL_UTF8) { // pre-NT or AP-utf8.exe (pish, thats my win98se,
// and without unicows support convert utf16toutf8
// and output raw bytes)
unsigned char *utf8_data = Convert_multibyteUTF16_to_UTF8(
unicode_string, asset_length * 6, asset_length - 14);
fprintf(stdout, "%s", utf8_data);
free(utf8_data);
utf8_data = NULL;
} else {
wchar_t *utf16_data = Convert_multibyteUTF16_to_wchar(
unicode_string, asset_length / 2, true);
// wchar_t* utf16_data = Convert_multibyteUTF16_to_wchar(unicode_string,
// (asset_length / 2) + 1, true);
APar_unicode_win32Printout(utf16_data, (char *)utf8_data);
free(utf16_data);
utf16_data = NULL;
}
#else
fprintf(stdout, "%s", utf8_data);
#endif
free(utf8_data);
utf8_data = NULL;
} else { // utf8
if (print_encoding) {
fprintf(stdout, " (utf8): ");
}
APar_fprintf_UTF8_data(unicode_string);
}
return;
}
///////////////////////////////////////////////////////////////////////////////////////
// embedded file extraction //
///////////////////////////////////////////////////////////////////////////////////////
/*----------------------
APar_Extract_uuid_binary_file
uuid_atom - pointer to the struct holding the information describing the
target atom originating_file - the full file path string to the parsed file
output_path - a (possibly null) string where the embedded file will be
extracted to
If the output path is a null pointer, create a new path derived from
originating file name & path - strip off the extension and use that as the base.
Read into memory the contents of that particular uuid atom. Glob onto the base
path the atom name and then the suffix that was embedded along with the file.
Write out the file to the now fully formed uuid_outfile path.
----------------------*/
void APar_Extract_uuid_binary_file(AtomicInfo *uuid_atom,
const char *originating_file,
char *output_path) {
uint32_t path_len = 0;
uint64_t atom_offsets = 0;
char *uuid_outfile = (char *)calloc(
1,
sizeof(char) * MAXPATHLEN + 1); // malloc a new string because it may be a
// cli arg for a specific output path
if (output_path == NULL) {
const char *orig_suffix = strrchr(originating_file, '.');
if (orig_suffix == NULL) {
fprintf(stdout,
"AP warning: a file extension for the input file was not "
"found.\n\tGlobbing onto original filename...\n");
path_len = strlen(originating_file);
memcpy(uuid_outfile, originating_file, path_len);
} else {
path_len = orig_suffix - originating_file;
memcpy(uuid_outfile, originating_file, path_len);
}
} else {
path_len = strlen(output_path);
memcpy(uuid_outfile, output_path, path_len);
}
char *uuid_payload =
(char *)calloc(1, sizeof(char) * (uuid_atom->AtomicLength - 36 + 1));
APar_readX(uuid_payload,
source_file,
uuid_atom->AtomicStart + 36,
uuid_atom->AtomicLength - 36);
uint32_t descrip_len = UInt32FromBigEndian(uuid_payload);
atom_offsets += 4 + descrip_len;
uint8_t suffix_len = (uint8_t)uuid_payload[atom_offsets];
char *file_suffix = (char *)calloc(1, sizeof(char) * suffix_len + 16);
memcpy(file_suffix, uuid_payload + atom_offsets + 1, suffix_len);
atom_offsets += 1 + suffix_len;
uint8_t mime_len = (uint8_t)uuid_payload[atom_offsets];
uint64_t mimetype_string = atom_offsets + 1;
atom_offsets += 1 + mime_len;
uint64_t bin_len = UInt32FromBigEndian(uuid_payload + atom_offsets);
atom_offsets += 4;
sprintf(uuid_outfile + path_len,
"-%s-uuid%s",
uuid_atom->uuid_ap_atomname,
file_suffix);
FILE *outfile = APar_OpenFile(uuid_outfile, "wb");
if (outfile != NULL) {
fwrite(uuid_payload + atom_offsets, (size_t)bin_len, 1, outfile);
fclose(outfile);
fprintf(stdout,
"Extracted uuid=%s attachment (mime-type=%s) to file: ",
uuid_atom->uuid_ap_atomname,
uuid_payload + mimetype_string);
APar_fprintf_UTF8_data(uuid_outfile);
fprintf(stdout, "\n");
}
free(uuid_payload);
uuid_payload = NULL;
free(uuid_outfile);
uuid_outfile = NULL;
free(file_suffix);
file_suffix = NULL;
return;
}
void APar_ExtractAAC_Artwork(short this_atom_num,
char *pic_output_path,
short artwork_count) {
char *base_outpath = (char *)malloc(sizeof(char) * MAXPATHLEN + 1);
if (snprintf(base_outpath,
MAXPATHLEN + 1,
"%s_artwork_%d",
pic_output_path,
artwork_count) > MAXPATHLEN) {
free(base_outpath);
return;
}
char *art_payload = (char *)malloc(
sizeof(char) * (parsedAtoms[this_atom_num].AtomicLength - 16) + 1);
memset(art_payload, 0, (parsedAtoms[this_atom_num].AtomicLength - 16) + 1);
APar_readX(art_payload,
source_file,
parsedAtoms[this_atom_num].AtomicStart + 16,
parsedAtoms[this_atom_num].AtomicLength - 16);
char *suffix = (char *)malloc(sizeof(char) * 5);
memset(suffix, 0, sizeof(char) * 5);
if (memcmp(art_payload, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) == 0) {
strcpy(suffix, ".png");
} else if (memcmp(art_payload, "\xFF\xD8\xFF", 3) == 0) {
strcpy(suffix, ".jpg");
}
strcat(base_outpath, suffix);
FILE *outfile = APar_OpenFile(base_outpath, "wb");
if (outfile != NULL) {
fwrite(art_payload,
(size_t)(parsedAtoms[this_atom_num].AtomicLength - 16),
1,
outfile);
fclose(outfile);
fprintf(stdout, "Extracted artwork to file: ");
APar_fprintf_UTF8_data(base_outpath);
fprintf(stdout, "\n");
}
free(base_outpath);
free(art_payload);
free(suffix);
return;
}
/*----------------------
APar_ImageExtractTest
buffer - pointer to raw image data
id3args - *currently unused* when testing raw image data from an image
file, results like mimetype & imagetype will be placed here
Loop through the ImageList array and see if the first few bytes in the image
data in buffer match any of the known image_binaryheader types listed. If it
does, and its png, do a further test to see if its type 0x01 which requires it
to be 32x32
----------------------*/
ImageFileFormatDefinition *APar_ImageExtractTest(char *buffer,
AdjunctArgs *id3args) {
ImageFileFormatDefinition *thisImage = NULL;
uint8_t total_image_tests = ImageListMembers();
for (uint8_t itest = 0; itest < total_image_tests; itest++) {
if (ImageList[itest].image_testbytes == 0) {
if (id3args != NULL) {
id3args->mimeArg = ImageList[itest].image_mimetype;
}
return &ImageList[itest];
} else if (memcmp(buffer,
ImageList[itest].image_binaryheader,
ImageList[itest].image_testbytes) == 0) {
if (id3args != NULL) {
id3args->mimeArg = ImageList[itest].image_mimetype;
if (id3args->pictype_uint8 == 0x01) {
if (memcmp(buffer + 16, "\x00\x00\x00\x20\x00\x00\x00\x20", 8) != 0 &&
itest != 2) {
id3args->pictype_uint8 = 0x02;
}
}
}
thisImage = &ImageList[itest];
break;
}
}
return thisImage;
}
/*----------------------
APar_Extract_ID3v2_file
id32_atom - pointer to the AtomicInfo ID32 atom that contains this while
ID3 tag (containing all the frames like APIC) frame_str - either APIC or GEOB
originfile - the originating mpeg-4 file that contains the ID32 atom
destination_folder - *currently not used* TODO: extract to this folder
id3args - *currently not used* TODO: extract by mimetype or imagetype or
description
Extracts (all) files of a particular frame type (APIC or GEOB - GEOB is
currently not implemented) out to a file next to the originating mpeg-4 file.
First, match frame_str to get the internal frameID number for APIC/GEOB frame.
Locate the .ext of the origin file, duplicate the path including the basename
(excluding the extension. Loop through the linked list of ID3v2Frame and search
for the internal frameID number. When an image is found, test the data that the
image contains and determine file extension from the ImageFileFormatDefinition
structure (containing some popular image format/extension definitions). In
combination with the file extension, use the image description and image type to
create the name of the output file. The image (which if was compressed on disc
was expanded when read in) and simply write out its data (stored in the 5th
member of the frame's field strings.
----------------------*/
void APar_Extract_ID3v2_file(AtomicInfo *id32_atom,
const char *frame_str,
const char *originfile,
const char *destination_folder,
AdjunctArgs *id3args) {
uint16_t iter = 0;
ID3v2Frame *eval_frame = NULL;
uint32_t basepath_len = 0;
char *extract_filename = NULL;
int frameID = MatchID3FrameIDstr(
frame_str, id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion);
int frameType = KnownFrames[frameID + 1].ID3v2_FrameType;
if (destination_folder == NULL) {
basepath_len = (strrchr(originfile, '.') - originfile);
}
if (frameType == ID3_ATTACHED_PICTURE_FRAME ||
frameType == ID3_ATTACHED_OBJECT_FRAME) {
if (id32_atom->ID32_TagInfo->ID3v2_FirstFrame == NULL)
return;
eval_frame = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
extract_filename = (char *)malloc(sizeof(char *) * MAXPATHLEN + 1);
while (eval_frame != NULL) {
if (frameType == eval_frame->ID3v2_FrameType) {
memset(extract_filename, 0, sizeof(char *) * MAXPATHLEN + 1);
memcpy(extract_filename, originfile, basepath_len);
iter++;
if (eval_frame->ID3v2_FrameType == ID3_ATTACHED_PICTURE_FRAME) {
ImageFileFormatDefinition *thisimage = APar_ImageExtractTest(
(eval_frame->ID3v2_Frame_Fields + 4)->field_string, NULL);
char *img_description =
APar_ConvertField_to_UTF8(eval_frame, ID3_DESCRIPTION_FIELD);
sprintf(
extract_filename + basepath_len,
"-img#%u-(desc=%s)-0x%02X%s",
iter,
img_description,
(uint8_t)((eval_frame->ID3v2_Frame_Fields + 2)->field_string[0]),
thisimage->image_fileextn);
if (img_description != NULL) {
free(img_description);
img_description = NULL;
}
} else {
char *obj_description =
APar_ConvertField_to_UTF8(eval_frame, ID3_DESCRIPTION_FIELD);
char *obj_filename =
APar_ConvertField_to_UTF8(eval_frame, ID3_FILENAME_FIELD);
sprintf(extract_filename + basepath_len,
"-obj#%u-(desc=%s)-%s",
iter,
obj_description,
obj_filename);
if (obj_description != NULL) {
free(obj_description);
obj_description = NULL;
}
if (obj_filename != NULL) {
free(obj_filename);
obj_filename = NULL;
}
}
FILE *extractfile = APar_OpenFile(extract_filename, "wb");
if (extractfile != NULL) {
fwrite((eval_frame->ID3v2_Frame_Fields + 4)->field_string,
(size_t)((eval_frame->ID3v2_Frame_Fields + 4)->field_length),
1,
extractfile);
fclose(extractfile);
fprintf(
stdout,
"Extracted %s to file: %s\n",
(frameType == ID3_ATTACHED_PICTURE_FRAME ? "artwork" : "object"),
extract_filename);
}
}
eval_frame = eval_frame->ID3v2_NextFrame;
}
}
if (extract_filename != NULL) {
free(extract_filename);
extract_filename = NULL;
}
return;
}
///////////////////////////////////////////////////////////////////////////////////////
// iTunes-style metadata listings //
///////////////////////////////////////////////////////////////////////////////////////
void APar_ExtractDataAtom(int this_atom_number) {
if (source_file != NULL) {
AtomicInfo *thisAtom = &parsedAtoms[this_atom_number];
char *parent_atom_name;
AtomicInfo parent_atom_stats = parsedAtoms[this_atom_number - 1];
parent_atom_name = parent_atom_stats.AtomicName;
uint32_t min_atom_datasize = 12;
uint32_t atom_header_size = 16;
if (thisAtom->AtomicClassification == EXTENDED_ATOM) {
if (thisAtom->uuid_style == UUID_DEPRECATED_FORM) {
min_atom_datasize += 4;
atom_header_size += 4;
} else {
min_atom_datasize = 36;
atom_header_size = 36;
}
}
if (thisAtom->AtomicLength > min_atom_datasize) {
char *data_payload = (char *)malloc(
sizeof(char) * (thisAtom->AtomicLength - atom_header_size + 1));
memset(data_payload,
0,
sizeof(char) * (thisAtom->AtomicLength - atom_header_size + 1));
APar_readX(data_payload,
source_file,
thisAtom->AtomicStart + atom_header_size,
thisAtom->AtomicLength - atom_header_size);
if (thisAtom->AtomicVerFlags == AtomFlags_Data_Text) {
if (thisAtom->AtomicLength < (atom_header_size + 4)) {
// tvnn was showing up with 4 chars instead of 3; easier to null it
// out for now
data_payload[thisAtom->AtomicLength - atom_header_size] = '\00';
}
APar_fprintf_UTF8_data(data_payload);
fprintf(stdout, "\n");
} else {
if ((memcmp(parent_atom_name, "trkn", 4) == 0) ||
(memcmp(parent_atom_name, "disk", 4) == 0)) {
if (UInt16FromBigEndian(data_payload + 4) != 0) {
fprintf(stdout,
"%u of %u\n",
UInt16FromBigEndian(data_payload + 2),
UInt16FromBigEndian(data_payload + 4));
} else {
fprintf(stdout, "%u\n", UInt16FromBigEndian(data_payload + 2));
}
} else if (strncmp(parent_atom_name, "gnre", 4) == 0) {
if (thisAtom->AtomicLength - atom_header_size <
3) { // oh, a 1byte int for genre number
char *genre_string =
GenreIntToString(UInt16FromBigEndian(data_payload));
if (genre_string != NULL) {
fprintf(stdout, "%s\n", genre_string);
} else {
fprintf(stdout,
" out of bound value - %u\n",
UInt16FromBigEndian(data_payload));
}
} else {
fprintf(stdout,
" out of bound value - %u\n",
UInt16FromBigEndian(data_payload));
}
} else if ((strncmp(parent_atom_name, "purl", 4) == 0) ||
(strncmp(parent_atom_name, "egid", 4) == 0)) {
fprintf(stdout, "%s\n", data_payload);
} else {
if (thisAtom->AtomicVerFlags == AtomFlags_Data_UInt &&
(thisAtom->AtomicLength <= 20 || thisAtom->AtomicLength == 24)) {
uint8_t bytes_rep =
(uint8_t)(thisAtom->AtomicLength - atom_header_size);
switch (bytes_rep) {
case 1: {
if ((memcmp(parent_atom_name, "cpil", 4) == 0) ||
(memcmp(parent_atom_name, "pcst", 4) == 0) ||
(memcmp(parent_atom_name, "pgap", 4) == 0)) {
if (data_payload[0] == 1) {
fprintf(stdout, "true\n");
} else {
fprintf(stdout, "false\n");
}
} else if (strncmp(parent_atom_name, "stik", 4) == 0) {
stiks *returned_stik =
MatchStikNumber((uint8_t)data_payload[0]);
if (returned_stik != NULL) {
fprintf(stdout, "%s\n", returned_stik->stik_string);
} else {
fprintf(
stdout, "Unknown value: %u\n", (uint8_t)data_payload[0]);
}
} else if (strncmp(parent_atom_name, "rtng", 4) ==
0) { // okay, this is definitely an 8-bit number
if (data_payload[0] == 2) {
fprintf(stdout, "Clean Content\n");
} else if (data_payload[0] != 0) {
fprintf(stdout, "Explicit Content\n");
} else {
fprintf(stdout, "Inoffensive\n");
}
} else {
fprintf(stdout, "%u\n", (uint8_t)data_payload[0]);
}
break;
}
case 2: { // tmpo
fprintf(stdout, "%u\n", UInt16FromBigEndian(data_payload));
break;
}
case 4: { // tves, tvsn
if (memcmp(parent_atom_name, "sfID", 4) == 0) {
sfIDs *this_store =
MatchStoreFrontNumber(UInt32FromBigEndian(data_payload));
if (this_store != NULL) {
fprintf(stdout,
"%s (%" PRIu32 ")\n",
this_store->storefront_string,
this_store->storefront_number);
} else {
fprintf(stdout,
"Unknown (%" PRIu32 ")\n",
UInt32FromBigEndian(data_payload));
}
} else {
fprintf(
stdout, "%" PRIu32 "\n", UInt32FromBigEndian(data_payload));
}
break;
}
case 8: {
fprintf(
stdout, "%" PRIu64 "\n", UInt64FromBigEndian(data_payload));
break;
}
}
} else if (thisAtom->AtomicClassification == EXTENDED_ATOM &&
thisAtom->AtomicVerFlags == AtomFlags_Data_uuid_binary &&
thisAtom->uuid_style == UUID_AP_SHA1_NAMESPACE) {
uint64_t offset_into_uuiddata = 0;
uint64_t descrip_len = UInt32FromBigEndian(data_payload);
offset_into_uuiddata += 4;
char *uuid_description =
(char *)calloc(1,
sizeof(char) * descrip_len +
16); // char uuid_description[descrip_len+1];
memcpy(uuid_description,
data_payload + offset_into_uuiddata,
descrip_len);
offset_into_uuiddata += descrip_len;
uint8_t suffix_len = (uint8_t)data_payload[offset_into_uuiddata];
offset_into_uuiddata += 1;
char *file_suffix =
(char *)calloc(1,
sizeof(char) * suffix_len +
16); // char file_suffix[suffix_len+1];
memcpy(
file_suffix, data_payload + offset_into_uuiddata, suffix_len);
offset_into_uuiddata += suffix_len;
uint8_t mime_len = (uint8_t)data_payload[offset_into_uuiddata];
offset_into_uuiddata += 1;
char *uuid_mimetype =
(char *)calloc(1,
sizeof(char) * mime_len +
16); // char uuid_mimetype[mime_len+1];
memcpy(
uuid_mimetype, data_payload + offset_into_uuiddata, mime_len);
fprintf(stdout,
"FILE%s; mime-type=%s; description=%s\n",
file_suffix,
uuid_mimetype,
uuid_description);
free(uuid_description);
uuid_description = NULL;
free(file_suffix);
file_suffix = NULL;
free(uuid_mimetype);
uuid_description = NULL;
} else { // purl & egid would end up here too, but Apple switched it
// to a text string (0x00), so gets taken care above
// explicitly
fprintf(stdout, "hex 0x");
for (int hexx = 1;
hexx <= (int)(thisAtom->AtomicLength - atom_header_size);
++hexx) {
fprintf(stdout, "%02X", (uint8_t)data_payload[hexx - 1]);
if ((hexx % 4) == 0 && hexx >= 4) {
fprintf(stdout, " ");
}
if ((hexx % 16) == 0 && hexx > 16) {
fprintf(stdout, "\n\t\t\t");
}
if (hexx == (int)(thisAtom->AtomicLength - atom_header_size)) {
fprintf(stdout, "\n");
}
}
} // end if AtomFlags_Data_UInt
}
free(data_payload);
data_payload = NULL;
}
}
}
return;
}
void APar_Print_iTunesData(const char *path,
char *output_path,
uint8_t supplemental_info,
uint8_t target_information,
AtomicInfo *ilstAtom) {
printBOM();
short artwork_count = 0;
if (ilstAtom == NULL) {
ilstAtom = APar_FindAtom("moov.udta.meta.ilst", false, SIMPLE_ATOM, 0);
if (ilstAtom == NULL)
return;
}
for (int i = ilstAtom->AtomicNumber; i < atom_number; i++) {
AtomicInfo *thisAtom = &parsedAtoms[i];
if (strncmp(thisAtom->AtomicName, "data", 4) ==
0) { // thisAtom->AtomicClassification == VERSIONED_ATOM) {
AtomicInfo *parent =
&parsedAtoms[APar_FindParentAtom(i, thisAtom->AtomicLevel)];
if ((thisAtom->AtomicVerFlags == AtomFlags_Data_Binary ||
thisAtom->AtomicVerFlags == AtomFlags_Data_Text ||
thisAtom->AtomicVerFlags == AtomFlags_Data_UInt) &&
target_information == PRINT_DATA) {
if (strncmp(parent->AtomicName, "----", 4) == 0) {
if (memcmp(parsedAtoms[parent->AtomicNumber + 2].AtomicName,
"name",
4) == 0) {
fprintf(stdout,
"Atom \"%s\" [%s;%s] contains: ",
parent->AtomicName,
parsedAtoms[parent->AtomicNumber + 1].ReverseDNSdomain,
parsedAtoms[parent->AtomicNumber + 2].ReverseDNSname);
APar_ExtractDataAtom(i);
}
} else if (memcmp(parent->AtomicName, "covr", 4) ==
0) { // libmp4v2 doesn't properly set artwork with the right
// flags (its all 0x00)
artwork_count++;
} else {
// converts iso8859 <20> in '<27>ART' to a 2byte utf8 <20> glyph; replaces
// libiconv conversion
memset(twenty_byte_buffer, 0, sizeof(char) * 20);
isolat1ToUTF8((unsigned char *)twenty_byte_buffer,
10,
(unsigned char *)parent->AtomicName,
4);
if (UnicodeOutputStatus == WIN32_UTF16) {
fprintf(stdout, "Atom \"");
APar_fprintf_UTF8_data(twenty_byte_buffer);
fprintf(stdout, "\" contains: ");
} else {
fprintf(stdout, "Atom \"%s\" contains: ", twenty_byte_buffer);
}
APar_ExtractDataAtom(i);
}
} else if (memcmp(parent->AtomicName, "covr", 4) == 0) {
artwork_count++;
if (target_information == EXTRACT_ARTWORK) {
APar_ExtractAAC_Artwork(
thisAtom->AtomicNumber, output_path, artwork_count);
}
}
if (thisAtom->AtomicLength <= 12) {
fprintf(
stdout,
"\n"); // (corrupted atom); libmp4v2 touching a file with copyright
}
}
}
if (artwork_count != 0 && target_information == PRINT_DATA) {
if (artwork_count == 1) {
fprintf(stdout,
"Atom \"covr\" contains: %i piece of artwork\n",
artwork_count);
} else {
fprintf(stdout,
"Atom \"covr\" contains: %i pieces of artwork\n",
artwork_count);
}
}
if (supplemental_info) {
fprintf(stdout, "---------------------------\n");
dynUpd.updage_by_padding = false;
// APar_DetermineDynamicUpdate(true); //gets the size of the padding
APar_Optimize(
true); // just to know if 'free' atoms can be considered padding, or (in
// the case of say a faac file) it's *just* 'free'
if (supplemental_info & 0x02) { // PRINT_FREE_SPACE
fprintf(stdout,
"free atom space: %" PRIu32 "\n",
APar_ProvideTallyForAtom("free"));
}
if (supplemental_info & 0x04) { // PRINT_PADDING_SPACE
if (!moov_atom_was_mooved) {
fprintf(stdout,
"padding available: %" PRIu64 " bytes\n",
dynUpd.padding_bytes);
} else {
fprintf(stdout, "padding available: 0 (reorg)\n");
}
}
if (supplemental_info & 0x08 &&
dynUpd.moov_udta_atom != NULL) { // PRINT_USER_DATA_SPACE
fprintf(stdout,
"user data space: %" PRIu64 "\n",
dynUpd.moov_udta_atom->AtomicLength);
}
if (supplemental_info & 0x10) { // PRINT_USER_DATA_SPACE
fprintf(stdout,
"media data space: %" PRIu32 "\n",
APar_ProvideTallyForAtom("mdat"));
}
}
return;
}
///////////////////////////////////////////////////////////////////////////////////////
// AP uuid metadata listings //
///////////////////////////////////////////////////////////////////////////////////////
void APar_Print_APuuidv5_contents(AtomicInfo *thisAtom) {
memset(twenty_byte_buffer, 0, sizeof(char) * 20);
isolat1ToUTF8((unsigned char *)twenty_byte_buffer,
10,
(unsigned char *)thisAtom->uuid_ap_atomname,
4);
fprintf(stdout, "Atom uuid=");
APar_print_uuid((ap_uuid_t *)thisAtom->AtomicName, false);
fprintf(stdout, " (AP uuid for \"");
APar_fprintf_UTF8_data(twenty_byte_buffer);
fprintf(stdout, "\") contains: ");
APar_ExtractDataAtom(thisAtom->AtomicNumber);
return;
}
void APar_Print_APuuid_deprecated_contents(AtomicInfo *thisAtom) {
memset(twenty_byte_buffer, 0, sizeof(char) * 20);
isolat1ToUTF8((unsigned char *)twenty_byte_buffer,
10,
(unsigned char *)thisAtom->AtomicName,
4);
if (UnicodeOutputStatus == WIN32_UTF16) {
fprintf(stdout, "Atom uuid=\"");
APar_fprintf_UTF8_data(twenty_byte_buffer);
fprintf(stdout, "\" contains: ");
} else {
fprintf(stdout, "Atom uuid=\"%s\" contains: ", twenty_byte_buffer);
}
APar_ExtractDataAtom(thisAtom->AtomicNumber);
return;
}
void APar_Print_APuuid_atoms(const char *path,
char *output_path,
uint8_t target_information) {
AtomicInfo *thisAtom = NULL;
printBOM();
AtomicInfo *metaAtom =
APar_FindAtom("moov.udta.meta", false, VERSIONED_ATOM, 0);
if (metaAtom == NULL)
return;
for (int i = metaAtom->NextAtomNumber; i < atom_number; i++) {
thisAtom = &parsedAtoms[i];
if (thisAtom->AtomicLevel <= metaAtom->AtomicLevel)
break; // we've gone too far
if (thisAtom->AtomicClassification == EXTENDED_ATOM) {
if (thisAtom->uuid_style == UUID_AP_SHA1_NAMESPACE) {
if (target_information == PRINT_DATA)
APar_Print_APuuidv5_contents(thisAtom);
if (target_information == EXTRACT_ALL_UUID_BINARYS &&
thisAtom->AtomicVerFlags == AtomFlags_Data_uuid_binary) {
APar_Extract_uuid_binary_file(thisAtom, path, output_path);
}
}
if (thisAtom->uuid_style == UUID_DEPRECATED_FORM &&
target_information == PRINT_DATA)
APar_Print_APuuid_deprecated_contents(thisAtom);
}
}
return;
}
///////////////////////////////////////////////////////////////////////////////////////
// 3GP asset metadata listings //
///////////////////////////////////////////////////////////////////////////////////////
void APar_PrintUnicodeAssest(char *unicode_string,
int asset_length) { // 3gp files
if (strncmp(unicode_string, "\xFE\xFF", 2) == 0) { // utf16
fprintf(stdout, " (utf16)] : ");
unsigned char *utf8_data = Convert_multibyteUTF16_to_UTF8(
unicode_string, (asset_length - 13) * 6, asset_length - 14);
#if defined(_WIN32) && !defined(__CYGWIN__)
if (GetVersion() & 0x80000000 ||
UnicodeOutputStatus ==
UNIVERSAL_UTF8) { // pre-NT or AP-utf8.exe (pish, thats my win98se,
// and without unicows support convert utf16toutf8
// and output raw bytes)
unsigned char *utf8_data = Convert_multibyteUTF16_to_UTF8(
unicode_string, (asset_length - 13) * 6, asset_length - 14);
fprintf(stdout, "%s", utf8_data);
free(utf8_data);
utf8_data = NULL;
} else {
wchar_t *utf16_data = Convert_multibyteUTF16_to_wchar(
unicode_string, (asset_length - 16) / 2, true);
APar_unicode_win32Printout(utf16_data, (char *)utf8_data);
free(utf16_data);
utf16_data = NULL;
}
#else
fprintf(stdout, "%s", utf8_data);
#endif
free(utf8_data);
utf8_data = NULL;
} else { // utf8
fprintf(stdout, " (utf8)] : ");
APar_fprintf_UTF8_data(unicode_string);
}
return;
}
void APar_Print_single_userdata_atomcontents(uint8_t track_num,
short userdata_atom,
bool quantum_listing) {
uint32_t box = UInt32FromBigEndian(parsedAtoms[userdata_atom].AtomicName);
char bitpacked_lang[3];
memset(bitpacked_lang, 0, 3);
unsigned char unpacked_lang[3];
uint32_t box_length = parsedAtoms[userdata_atom].AtomicLength;
char *box_data = (char *)malloc(sizeof(char) * box_length);
memset(box_data, 0, sizeof(char) * box_length);
switch (box) {
case 0x7469746C: //'titl'
case 0x64736370: //'dscp'
case 0x63707274: //'cprt'
case 0x70657266: //'perf'
case 0x61757468: //'auth'
case 0x676E7265: //'gnre'
case 0x616C626D: //'albm'
{
APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
uint16_t packed_lang =
APar_read16(bitpacked_lang,
source_file,
parsedAtoms[userdata_atom].AtomicStart + 12);
APar_UnpackLanguage(unpacked_lang, packed_lang);
APar_readX(
box_data,
source_file,
parsedAtoms[userdata_atom].AtomicStart + 14,
box_length -
14); // 4bytes length, 4 bytes name, 4 bytes flags, 2 bytes lang
// get tracknumber *after* we read the whole tag; if we have a utf16 tag, it
// will have a BOM, indicating if we have to search for 2 NULLs or a utf8
// single NULL, then the ****optional**** tracknumber
uint16_t track_num = 1000; // tracknum is a uint8_t, so setting it > 256
// means a number wasn't found
if (box ==
0x616C626D) { //'albm' has an *optional* uint8_t at the end for
// tracknumber; if the last byte in the tag is not
// 0, then it must be the optional tracknum (or a
// non-compliant, non-NULL-terminated string). This
// byte is the length - (14 bytes +1tracknum) or -15
if (box_data[box_length - 15] != 0) {
track_num = (uint16_t)box_data[box_length - 15];
box_data[box_length - 15] =
0; // NULL out the last byte if found to be not 0 - it will impact
// unicode conversion if it remains
}
}
fprintf(stdout, "[lang=%s", unpacked_lang);
APar_PrintUnicodeAssest(box_data, box_length);
if (box == 0x616C626D && track_num != 1000) {
fprintf(stdout, " | Track: %u", track_num);
}
fprintf(stdout, "\n");
break;
}
case 0x72746E67: //'rtng'
{
APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
APar_readX(
box_data, source_file, parsedAtoms[userdata_atom].AtomicStart + 12, 4);
fprintf(stdout, "[Rating Entity=%s", box_data);
memset(box_data, 0, box_length);
APar_readX(
box_data, source_file, parsedAtoms[userdata_atom].AtomicStart + 16, 4);
fprintf(stdout, " | Criteria=%s", box_data);
uint16_t packed_lang =
APar_read16(bitpacked_lang,
source_file,
parsedAtoms[userdata_atom].AtomicStart + 20);
APar_UnpackLanguage(unpacked_lang, packed_lang);
fprintf(stdout, " lang=%s", unpacked_lang);
memset(box_data, 0, box_length);
APar_readX(box_data,
source_file,
parsedAtoms[userdata_atom].AtomicStart + 22,
box_length - 8);
APar_PrintUnicodeAssest(box_data, box_length - 8);
fprintf(stdout, "\n");
break;
}
case 0x636C7366: //'clsf'
{
APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
APar_readX(
box_data,
source_file,
parsedAtoms[userdata_atom].AtomicStart + 12,
box_length -
12); // 4bytes length, 4 bytes name, 4 bytes flags, 2 bytes lang
fprintf(stdout, "[Classification Entity=%s", box_data);
fprintf(stdout, " | Index=%u", UInt16FromBigEndian(box_data + 4));
uint16_t packed_lang =
APar_read16(bitpacked_lang,
source_file,
parsedAtoms[userdata_atom].AtomicStart + 18);
APar_UnpackLanguage(unpacked_lang, packed_lang);
fprintf(stdout, " lang=%s", unpacked_lang);
APar_PrintUnicodeAssest(box_data + 8, box_length - 8);
fprintf(stdout, "\n");
break;
}
case 0x6B797764: //'kywd'
{
APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
uint64_t box_offset = 12;
uint16_t packed_lang =
APar_read16(bitpacked_lang,
source_file,
parsedAtoms[userdata_atom].AtomicStart + box_offset);
box_offset += 2;
APar_UnpackLanguage(unpacked_lang, packed_lang);
uint8_t keyword_count = APar_read8(
source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset);
box_offset++;
fprintf(stdout, "[Keyword count=%u", keyword_count);
fprintf(stdout, " lang=%s]", unpacked_lang);
char *keyword_data = (char *)malloc(sizeof(char) * box_length * 2);
for (uint8_t x = 1; x <= keyword_count; x++) {
memset(keyword_data, 0, box_length * 2);
uint8_t keyword_length = APar_read8(
source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset);
box_offset++;
APar_readX(keyword_data,
source_file,
parsedAtoms[userdata_atom].AtomicStart + box_offset,
keyword_length);
box_offset += keyword_length;
APar_SimplePrintUnicodeAssest(keyword_data, keyword_length, true);
}
free(keyword_data);
keyword_data = NULL;
fprintf(stdout, "\n");
break;
}
case 0x6C6F6369: //'loci' aka The Most Heinous Metadata Atom Every Invented -
// decimal meters? fictional location? Astromical Body? Say I
// shoot it on the International Space Station? That isn't a
// Astronimical Body. And 16.16 alt only goes up to 20.3
// miles (because of negatives, its really 15.15) & the ISS
// is
// at 230 miles. Oh, pish.... what ever shall I do? I fear I
// am on the horns of a dilema.
{
APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
uint64_t box_offset = 12;
uint16_t packed_lang =
APar_read16(bitpacked_lang,
source_file,
parsedAtoms[userdata_atom].AtomicStart + box_offset);
box_offset += 2;
APar_UnpackLanguage(unpacked_lang, packed_lang);
APar_readX(box_data,
source_file,
parsedAtoms[userdata_atom].AtomicStart + box_offset,
box_length);
fprintf(stdout, "[lang=%s] ", unpacked_lang);
// the length of the location string is unknown (max is box lenth), but the
// long/lat/alt/body/notes needs to be retrieved. test if the location
// string is utf16; if so search for 0x0000 (or if utf8, find the first
// NULL).
if (strncmp(box_data, "\xFE\xFF", 2) == 0) {
box_offset += 2 * widechar_len(box_data, box_length) +
2; //*2 for utf16 (double-byte); +2 for the terminating NULL
fprintf(stdout, "(utf16) ");
} else {
fprintf(stdout, "(utf8) ");
box_offset += strlen(box_data) + 1; //+1 for the terminating NULL
}
fprintf(stdout, "Location: ");
APar_SimplePrintUnicodeAssest(box_data, box_length, false);
uint8_t location_role = APar_read8(
source_file, parsedAtoms[userdata_atom].AtomicStart + box_offset);
box_offset++;
switch (location_role) {
case 0: {
fprintf(stdout, " (Role: shooting location) ");
break;
}
case 1: {
fprintf(stdout, " (Role: real location) ");
break;
}
case 2: {
fprintf(stdout, " (Role: fictional location) ");
break;
}
default: {
fprintf(stdout, " (Role: [reserved]) ");
break;
}
}
char *float_buffer = (char *)malloc(sizeof(char) * 5);
memset(float_buffer, 0, 5);
fprintf(stdout,
"[Long %lf",
fixed_point_16x16bit_to_double(APar_read32(
float_buffer,
source_file,
parsedAtoms[userdata_atom].AtomicStart + box_offset)));
box_offset += 4;
fprintf(stdout,
" Lat %lf",
fixed_point_16x16bit_to_double(APar_read32(
float_buffer,
source_file,
parsedAtoms[userdata_atom].AtomicStart + box_offset)));
box_offset += 4;
fprintf(stdout,
" Alt %lf ",
fixed_point_16x16bit_to_double(APar_read32(
float_buffer,
source_file,
parsedAtoms[userdata_atom].AtomicStart + box_offset)));
box_offset += 4;
free(float_buffer);
float_buffer = NULL;
if (box_offset < box_length) {
fprintf(stdout, " Body: ");
APar_SimplePrintUnicodeAssest(
box_data + box_offset - 14, box_length - box_offset, false);
if (strncmp(box_data + box_offset - 14, "\xFE\xFF", 2) == 0) {
box_offset +=
2 * widechar_len(box_data + box_offset - 14,
box_length - box_offset) +
2; //*2 for utf16 (double-byte); +2 for the terminating NULL
} else {
box_offset += strlen(box_data + box_offset - 14) +
1; //+1 for the terminating NULL
}
}
fprintf(stdout, "]");
if (box_offset < box_length) {
fprintf(stdout, " Notes: ");
APar_SimplePrintUnicodeAssest(
box_data + box_offset - 14, box_length - box_offset, false);
}
fprintf(stdout, "\n");
break;
}
case 0x79727263: //'yrrc'
{
APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
uint16_t recording_year =
APar_read16(bitpacked_lang,
source_file,
parsedAtoms[userdata_atom].AtomicStart + 12);
fprintf(stdout, ": %u\n", recording_year);
break;
}
case 0x6E616D65: //'name'
{
APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
APar_fprintf_UTF8_data(": ");
APar_readX(
box_data,
source_file,
parsedAtoms[userdata_atom].AtomicStart + 8,
box_length -
8); // 4bytes length, 4 bytes name, 4 bytes flags, 2 bytes lang
APar_fprintf_UTF8_data(box_data);
APar_fprintf_UTF8_data("\n");
break;
}
case 0x686E7469: //'hnti'
{
APar_Mark_UserData_area(track_num, userdata_atom, quantum_listing);
APar_readX(
box_data,
source_file,
parsedAtoms[userdata_atom + 1].AtomicStart + 8,
box_length -
8); // 4bytes length, 4 bytes name, 4 bytes flags, 2 bytes lang
fprintf(stdout, "for %s:\n", parsedAtoms[userdata_atom + 1].AtomicName);
APar_fprintf_UTF8_data(box_data);
break;
}
default: {
break;
}
}
return;
}
///////////////////////////////////////////////////////////////////////////////////////
// id3 displaying functions //
///////////////////////////////////////////////////////////////////////////////////////
void APar_Print_ID3TextField(ID3v2Frame *textframe,
ID3v2Fields *textfield,
bool linefeed = false) {
// this won't accommodate id3v2.4's multiple strings separated by NULLs
if (textframe->ID3v2_Frame_Fields->field_string[0] ==
TE_LATIN1) { // all frames that have text encodings have the encoding as
// the first field
if (textfield->field_length > 0) {
char *conv_buffer =
(char *)calloc(1, sizeof(char *) * (textfield->field_length * 4) + 2);
isolat1ToUTF8((unsigned char *)conv_buffer,
sizeof(char *) * (textfield->field_length * 4) + 2,
(unsigned char *)textfield->field_string,
textfield->field_length);
fprintf(stdout, "%s", conv_buffer);
free(conv_buffer);
conv_buffer = NULL;
}
} else if (textframe->ID3v2_Frame_Fields->field_string[0] ==
TE_UTF16LE_WITH_BOM) { // technically AP *writes* uff16LE here, but
// based on BOM, it could be utf16BE
if (textfield->field_length > 2) {
char *conv_buffer =
(char *)calloc(1, sizeof(char *) * (textfield->field_length * 2) + 2);
if (strncmp(textfield->field_string, "\xFF\xFE", 2) == 0) {
UTF16LEToUTF8((unsigned char *)conv_buffer,
sizeof(char *) * (textfield->field_length * 4) + 2,
(unsigned char *)textfield->field_string + 2,
textfield->field_length);
fprintf(stdout, "%s", conv_buffer);
} else {
UTF16BEToUTF8((unsigned char *)conv_buffer,
sizeof(char *) * (textfield->field_length * 4) + 2,
(unsigned char *)textfield->field_string + 2,
textfield->field_length);
fprintf(stdout, "%s", conv_buffer);
}
free(conv_buffer);
conv_buffer = NULL;
}
} else if (textframe->ID3v2_Frame_Fields->field_string[0] ==
TE_UTF16BE_NO_BOM) {
if (textfield->field_length > 0) {
char *conv_buffer =
(char *)calloc(1, sizeof(char *) * (textfield->field_length * 2) + 2);
UTF16BEToUTF8((unsigned char *)conv_buffer,
sizeof(char *) * (textfield->field_length * 4) + 2,
(unsigned char *)textfield->field_string,
textfield->field_length);
fprintf(stdout, "%s", conv_buffer);
free(conv_buffer);
conv_buffer = NULL;
}
} else if (textframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF8) {
fprintf(stdout, "%s", textfield->field_string);
} else {
fprintf(stdout,
"(unknown type: 0x%X",
(uint8_t)textframe->ID3v2_Frame_Fields->field_string[0]);
}
if (linefeed)
fprintf(stdout, "\n");
return;
}
const char *APar_GetTextEncoding(ID3v2Frame *aframe, ID3v2Fields *textfield) {
const char *text_encoding = NULL;
if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_LATIN1)
text_encoding = "latin1";
if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF16BE_NO_BOM) {
if (strncmp(textfield->field_string, "\xFF\xFE", 2) == 0) {
text_encoding = "utf16le";
} else if (strncmp(textfield->field_string, "\xFE\xFF", 2) == 0) {
text_encoding = "utf16be";
}
}
if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF16LE_WITH_BOM)
text_encoding = "utf16le";
if (aframe->ID3v2_Frame_Fields->field_string[0] == TE_UTF8)
text_encoding = "utf8";
return text_encoding;
}
void APar_Print_ID3v2_tags(AtomicInfo *id32_atom) {
// TODO properly printout latin1 for fields like owner
// TODO for binary fields (like GRID group data) scan through to see if it
// needs to be printed in hex fprintf(stdout, "Maj.Min.Rev version
// was 2.%u.%u\n", id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion,
// id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion);
char *id32_level = (char *)calloc(1, sizeof(char *) * 16);
if (id32_atom->AtomicLevel == 2) {
memcpy(id32_level, "file level", 10);
} else if (id32_atom->AtomicLevel == 3) {
memcpy(id32_level, "movie level", 11);
} else if (id32_atom->AtomicLevel == 4) {
sprintf(id32_level,
"track #%u",
1); // unimplemented; need to pass a variable here
}
unsigned char unpacked_lang[3];
APar_UnpackLanguage(unpacked_lang, id32_atom->AtomicLanguage);
if (id32_atom->ID32_TagInfo->ID3v2_FirstFrame != NULL) {
fprintf(stdout,
"ID32 atom [lang=%s] at %s contains an ID3v2.%u.%u tag (%u tags, "
"%u bytes):\n",
unpacked_lang,
id32_level,
id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion,
id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion,
id32_atom->ID32_TagInfo->ID3v2_FrameCount,
id32_atom->ID32_TagInfo->ID3v2Tag_Length);
} else {
fprintf(stdout,
"ID32 atom [lang=%s] at %s contains an ID3v2.%u.%u tag. ",
unpacked_lang,
id32_level,
id32_atom->ID32_TagInfo->ID3v2Tag_MajorVersion,
id32_atom->ID32_TagInfo->ID3v2Tag_RevisionVersion);
if (ID3v2_TestTagFlag(id32_atom->ID32_TagInfo->ID3v2Tag_Flags,
ID32_TAGFLAG_UNSYNCRONIZATION)) {
fprintf(stdout,
"Unsynchronized flag set. Unsupported. No tags read. %" PRIu32
" bytes.\n",
id32_atom->ID32_TagInfo->ID3v2Tag_Length);
}
}
ID3v2Frame *target_frameinfo = id32_atom->ID32_TagInfo->ID3v2_FirstFrame;
while (target_frameinfo != NULL) {
if (ID3v2_TestFrameFlag(target_frameinfo->ID3v2_Frame_Flags,
ID32_FRAMEFLAG_GROUPING) &&
target_frameinfo &&
target_frameinfo->ID3v2_FrameType != ID3_GROUP_ID_FRAME) {
fprintf(stdout,
" Tag: %s GID=0x%02X \"%s\" ",
target_frameinfo->ID3v2_Frame_Namestr,
target_frameinfo->ID3v2_Frame_GroupingSymbol,
KnownFrames[target_frameinfo->ID3v2_Frame_ID + 1]
.ID3V2_FrameDescription);
} else {
fprintf(stdout,
" Tag: %s \"%s\" ",
target_frameinfo->ID3v2_Frame_Namestr,
KnownFrames[target_frameinfo->ID3v2_Frame_ID + 1]
.ID3V2_FrameDescription);
}
uint8_t frame_comp_idx =
GetFrameCompositionDescription(target_frameinfo->ID3v2_FrameType);
if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
ID3_UNKNOWN_FRAME) {
fprintf(stdout,
"(unknown frame) %" PRIu32 " bytes\n",
target_frameinfo->ID3v2_Frame_Fields->field_length);
} else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
ID3_TEXT_FRAME) {
ID3v2Fields *atextfield = target_frameinfo->ID3v2_Frame_Fields + 1;
if (target_frameinfo->textfield_tally > 1) {
fprintf(stdout,
"(%s) : { ",
APar_GetTextEncoding(target_frameinfo,
target_frameinfo->ID3v2_Frame_Fields + 1));
} else {
fprintf(stdout,
"(%s) : ",
APar_GetTextEncoding(target_frameinfo,
target_frameinfo->ID3v2_Frame_Fields + 1));
}
while (true) {
if (target_frameinfo->textfield_tally > 1) {
fprintf(stdout, "\"");
}
if (target_frameinfo->ID3v2_Frame_ID == ID3v2_FRAME_CONTENTTYPE) {
char *genre_string = NULL;
int genre_idx =
(int)strtol(atextfield->field_string, &genre_string, 10);
if (genre_string != atextfield->field_string) {
genre_string = ID3GenreIntToString(genre_idx);
if (target_frameinfo->textfield_tally == 1) {
fprintf(stdout, "%s\n", ID3GenreIntToString(genre_idx));
} else {
fprintf(stdout, "%s", ID3GenreIntToString(genre_idx));
}
} else {
APar_Print_ID3TextField(
target_frameinfo,
atextfield,
target_frameinfo->textfield_tally == 1 ? true : false);
}
} else if (target_frameinfo->ID3v2_Frame_ID == ID3v2_FRAME_COPYRIGHT) {
APar_fprintf_UTF8_data("\xC2\xA9 ");
APar_Print_ID3TextField(
target_frameinfo,
atextfield,
target_frameinfo->textfield_tally == 1 ? true : false);
} else if (target_frameinfo->ID3v2_Frame_ID == ID3v2_FRAME_PRODNOTICE) {
APar_fprintf_UTF8_data("\xE2\x84\x97 ");
APar_Print_ID3TextField(
target_frameinfo,
atextfield,
target_frameinfo->textfield_tally == 1 ? true : false);
} else {
APar_Print_ID3TextField(
target_frameinfo,
atextfield,
target_frameinfo->textfield_tally == 1 ? true : false);
}
if (target_frameinfo->textfield_tally > 1) {
fprintf(stdout, "\"");
} else {
break;
}
atextfield = atextfield->next_field;
if (atextfield == NULL) {
fprintf(stdout, " }\n");
break;
} else {
fprintf(stdout, ", ");
}
}
} else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
ID3_TEXT_FRAME_USERDEF) {
fprintf(stdout, "(user-defined text frame) ");
fprintf(stdout, "%u fields\n", target_frameinfo->ID3v2_FieldCount);
} else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
ID3_URL_FRAME) {
fprintf(stdout,
"(url frame) : %s\n",
(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
fprintf(stdout, "%u fields\n", target_frameinfo->ID3v2_FieldCount);
} else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
ID3_URL_FRAME_USERDEF) {
fprintf(stdout, "(user-defined url frame) ");
fprintf(stdout, "%u fields\n", target_frameinfo->ID3v2_FieldCount);
} else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
ID3_UNIQUE_FILE_ID_FRAME) {
if (test_limited_ascii(
(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string,
(target_frameinfo->ID3v2_Frame_Fields + 1)->field_length)) {
fprintf(stdout,
"(owner='%s') : %s\n",
target_frameinfo->ID3v2_Frame_Fields->field_string,
(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
} else {
fprintf(stdout,
"(owner='%s') : 0x",
target_frameinfo->ID3v2_Frame_Fields->field_string);
for (uint32_t hexidx = 0;
hexidx < (target_frameinfo->ID3v2_Frame_Fields + 1)->field_length;
hexidx++) {
fprintf(stdout,
"%02X",
(uint8_t)(target_frameinfo->ID3v2_Frame_Fields + 1)
->field_string[hexidx]);
}
fprintf(stdout, "\n");
}
} else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
ID3_CD_ID_FRAME) { // TODO: print hex representation
uint8_t tracklistings = 0;
if (target_frameinfo->ID3v2_Frame_Fields->field_length >= 16) {
tracklistings = target_frameinfo->ID3v2_Frame_Fields->field_length / 8;
fprintf(stdout,
"(Music CD Identifier) : Entries for %u tracks + leadout "
"track.\n Hex: 0x",
tracklistings - 1);
} else {
fprintf(stdout,
"(Music CD Identifier) : Unknown format (less then 16 "
"bytes).\n Hex: 0x");
}
for (uint16_t hexidx = 1;
hexidx < target_frameinfo->ID3v2_Frame_Fields->field_length + 1;
hexidx++) {
fprintf(stdout,
"%02X",
(uint8_t)target_frameinfo->ID3v2_Frame_Fields
->field_string[hexidx - 1]);
if (hexidx % 4 == 0)
fprintf(stdout, " ");
}
fprintf(stdout, "\n");
} else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
ID3_DESCRIBED_TEXT_FRAME) {
fprintf(stdout,
"(%s, lang=%s, desc[",
APar_GetTextEncoding(target_frameinfo,
target_frameinfo->ID3v2_Frame_Fields + 2),
(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
APar_Print_ID3TextField(target_frameinfo,
target_frameinfo->ID3v2_Frame_Fields + 2);
fprintf(stdout, "]) : ");
APar_Print_ID3TextField(
target_frameinfo, target_frameinfo->ID3v2_Frame_Fields + 3, true);
} else if (FrameTypeConstructionList[frame_comp_idx].ID3_FrameType ==
ID3_ATTACHED_PICTURE_FRAME) {
fprintf(
stdout,
"(type=0x%02X-'%s', mimetype=%s, %s, desc[",
(uint8_t)(target_frameinfo->ID3v2_Frame_Fields + 2)->field_string[0],
ImageTypeList[(uint8_t)(target_frameinfo->ID3v2_Frame_Fields + 2)
->field_string[0]]
.imagetype_str,
(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string,
APar_GetTextEncoding(target_frameinfo,
target_frameinfo->ID3v2_Frame_Fields + 1));
APar_Print_ID3TextField(target_frameinfo,
target_frameinfo->ID3v2_Frame_Fields + 3);
if (ID3v2_TestFrameFlag(target_frameinfo->ID3v2_Frame_Flags,
ID32_FRAMEFLAG_COMPRESSED)) {
fprintf(stdout,
"]) : %" PRIu32 " bytes (%" PRIu32 " compressed)\n",
(target_frameinfo->ID3v2_Frame_Fields + 4)->field_length,
target_frameinfo->ID3v2_Frame_Length);
} else {
fprintf(stdout,
"]) : %" PRIu32 " bytes\n",
(target_frameinfo->ID3v2_Frame_Fields + 4)->field_length);
}
} else if (target_frameinfo->ID3v2_FrameType == ID3_ATTACHED_OBJECT_FRAME) {
fprintf(stdout, "(filename=");
APar_Print_ID3TextField(target_frameinfo,
target_frameinfo->ID3v2_Frame_Fields + 2);
fprintf(stdout,
", mimetype=%s, desc[",
(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
APar_Print_ID3TextField(target_frameinfo,
target_frameinfo->ID3v2_Frame_Fields + 3);
if (ID3v2_TestFrameFlag(target_frameinfo->ID3v2_Frame_Flags,
ID32_FRAMEFLAG_COMPRESSED)) {
fprintf(stdout,
"]) : %" PRIu32 " bytes (%" PRIu32 " compressed)\n",
(target_frameinfo->ID3v2_Frame_Fields + 4)->field_length,
target_frameinfo->ID3v2_Frame_Length);
} else {
fprintf(stdout,
"]) : %" PRIu32 " bytes\n",
(target_frameinfo->ID3v2_Frame_Fields + 4)->field_length);
}
} else if (target_frameinfo->ID3v2_FrameType == ID3_GROUP_ID_FRAME) {
fprintf(
stdout,
"(owner='%s') : 0x%02X",
target_frameinfo->ID3v2_Frame_Fields->field_string,
(uint8_t)(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string[0]);
if ((target_frameinfo->ID3v2_Frame_Fields + 2)->field_length > 0) {
fprintf(stdout,
"; groupdata='%s'\n",
(target_frameinfo->ID3v2_Frame_Fields + 2)->field_string);
} else {
fprintf(stdout, "\n");
}
} else if (target_frameinfo->ID3v2_FrameType == ID3_PRIVATE_FRAME) {
fprintf(stdout,
"(owner='%s') : %s\n",
target_frameinfo->ID3v2_Frame_Fields->field_string,
(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
} else if (target_frameinfo->ID3v2_FrameType == ID3_SIGNATURE_FRAME) {
fprintf(stdout,
"{GID=0x%02X) : %s\n",
(uint8_t)target_frameinfo->ID3v2_Frame_Fields->field_string[0],
(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string);
} else if (target_frameinfo->ID3v2_FrameType == ID3_PLAYCOUNTER_FRAME) {
if (target_frameinfo->ID3v2_Frame_Fields->field_length == 4) {
fprintf(stdout,
": %" PRIu32 "\n",
syncsafe32_to_UInt32(
target_frameinfo->ID3v2_Frame_Fields->field_string));
} else if (target_frameinfo->ID3v2_Frame_Fields->field_length > 4) {
fprintf(stdout,
": %" PRIu64 "\n",
syncsafeXX_to_UInt64(
target_frameinfo->ID3v2_Frame_Fields->field_string,
target_frameinfo->ID3v2_Frame_Fields->field_length));
}
} else if (target_frameinfo->ID3v2_FrameType == ID3_POPULAR_FRAME) {
fprintf(
stdout,
"(owner='%s') : %u",
target_frameinfo->ID3v2_Frame_Fields->field_string,
(uint8_t)(target_frameinfo->ID3v2_Frame_Fields + 1)->field_string[0]);
if ((target_frameinfo->ID3v2_Frame_Fields + 2)->field_length > 0) {
if ((target_frameinfo->ID3v2_Frame_Fields + 2)->field_length == 4) {
fprintf(
stdout,
"; playcount=%" PRIu32 "\n",
syncsafe32_to_UInt32(
(target_frameinfo->ID3v2_Frame_Fields + 2)->field_string));
} else if ((target_frameinfo->ID3v2_Frame_Fields + 2)->field_length >
4) {
fprintf(
stdout,
"; playcount=%" PRIu64 "\n",
syncsafeXX_to_UInt64(
(target_frameinfo->ID3v2_Frame_Fields + 2)->field_string,
(target_frameinfo->ID3v2_Frame_Fields + 2)->field_length));
} else {
fprintf(stdout,
"\n"); // don't know what it was supposed to be, so skip it
}
} else {
fprintf(stdout, "\n");
}
} else {
fprintf(stdout,
" [idx=%u;%d]\n",
frame_comp_idx,
FrameTypeConstructionList[frame_comp_idx].ID3_FrameType);
}
target_frameinfo = target_frameinfo->ID3v2_NextFrame;
}
free(id32_level);
id32_level = NULL;
return;
}
///////////////////////////////////////////////////////////////////////////////////////
// metadata scheme searches //
///////////////////////////////////////////////////////////////////////////////////////
void APar_Print_metachild_atomcontents(uint8_t track_num,
short metachild_atom,
bool quantum_listing) {
if (memcmp(parsedAtoms[metachild_atom].AtomicName, "ID32", 4) == 0) {
APar_ID32_ScanID3Tag(source_file, &parsedAtoms[metachild_atom]);
APar_Print_ID3v2_tags(&parsedAtoms[metachild_atom]);
}
return;
}
void APar_PrintMetaChildren(AtomicInfo *metaAtom,
AtomicInfo *hdlrAtom,
bool quantum_listing) {
if (metaAtom != NULL && hdlrAtom != NULL) {
if (hdlrAtom->ancillary_data == 0x49443332) {
for (int i = metaAtom->NextAtomNumber; i < atom_number; i++) {
if (parsedAtoms[i].AtomicLevel <= metaAtom->AtomicLevel)
break; // we've gone too far
if (parsedAtoms[i].AtomicLevel == metaAtom->AtomicLevel + 1)
APar_Print_metachild_atomcontents(0, i, quantum_listing);
}
}
}
return;
}
void APar_PrintID32Metadata(bool quantum_listing) {
uint8_t total_tracks = 0;
uint8_t a_track = 0;
AtomicInfo *metaAtom = NULL;
AtomicInfo *metahandlerAtom = NULL;
char trackmeta_atom_path[50];
printBOM();
// file level
metaAtom = APar_FindAtom("meta", false, VERSIONED_ATOM, 0);
metahandlerAtom = APar_FindAtom("meta.hdlr", false, VERSIONED_ATOM, 0);
APar_PrintMetaChildren(metaAtom, metahandlerAtom, quantum_listing);
// movie level
metaAtom = APar_FindAtom("moov.meta", false, VERSIONED_ATOM, 0);
metahandlerAtom = APar_FindAtom("moov.meta.hdlr", false, VERSIONED_ATOM, 0);
APar_PrintMetaChildren(metaAtom, metahandlerAtom, quantum_listing);
// track level
APar_FindAtomInTrack(total_tracks,
a_track,
NULL); // With track_num set to 0, it will return the
// total trak atom into total_tracks here.
for (uint8_t i = 1; i <= total_tracks; i++) {
memset(&trackmeta_atom_path, 0, 50);
sprintf(trackmeta_atom_path, "moov.trak[%u].meta", i);
metaAtom = APar_FindAtom(trackmeta_atom_path, false, VERSIONED_ATOM, 0);
sprintf(trackmeta_atom_path, "moov.trak[%u].meta.hdlr", i);
metahandlerAtom =
APar_FindAtom(trackmeta_atom_path, false, VERSIONED_ATOM, 0);
APar_PrintMetaChildren(metaAtom, metahandlerAtom, quantum_listing);
}
return;
}
/*----------------------
APar_Print_ISO_UserData_per_track
quantum_listing - controls whether to simply print each asset, or
preface each asset with "movie level"
This will only show what is under moov.trak.udta atoms (not moov.udta). Get
the total number of tracks; construct the moov.trak[index].udta path to find,
then if the atom after udta is of a greater level, read in from
the file & print out what it contains.
----------------------*/
void APar_PrintUserDataAssests(bool quantum_listing) {
printBOM();
AtomicInfo *udtaAtom = APar_FindAtom("moov.udta", false, SIMPLE_ATOM, 0);
if (udtaAtom != NULL) {
for (int i = udtaAtom->NextAtomNumber; i < atom_number; i++) {
if (parsedAtoms[i].AtomicLevel <= udtaAtom->AtomicLevel)
break; // we've gone too far
if (parsedAtoms[i].AtomicLevel == udtaAtom->AtomicLevel + 1)
APar_Print_single_userdata_atomcontents(0, i, quantum_listing);
}
}
APar_PrintID32Metadata(quantum_listing);
APar_Print_APuuid_atoms(NULL, NULL, PRINT_DATA);
return;
}
/*----------------------
APar_Print_ISO_UserData_per_track
This will only show what is under moov.trak.udta atoms (not moov.udta). Get
the total number of tracks; construct the moov.trak[index].udta path to find,
then if the atom after udta is of a greater level, read in from
the file & print out what it contains.
----------------------*/
void APar_Print_ISO_UserData_per_track() {
uint8_t total_tracks = 0;
uint8_t a_track = 0; // unused
short a_trak_atom = 0;
char iso_atom_path[400];
AtomicInfo *trak_udtaAtom = NULL;
APar_FindAtomInTrack(total_tracks,
a_track,
NULL); // With track_num set to 0, it will return the
// total trak atom into total_tracks here.
for (uint8_t i = 1; i <= total_tracks; i++) {
memset(&iso_atom_path, 0, 400);
sprintf(iso_atom_path, "moov.trak[%u].udta", i);
trak_udtaAtom = APar_FindAtom(iso_atom_path, false, SIMPLE_ATOM, 0);
if (trak_udtaAtom != NULL &&
parsedAtoms[trak_udtaAtom->NextAtomNumber].AtomicLevel ==
trak_udtaAtom->AtomicLevel + 1) {
a_trak_atom = trak_udtaAtom->NextAtomNumber;
while (
parsedAtoms[a_trak_atom].AtomicLevel >
trak_udtaAtom
->AtomicLevel) { // only work on moov.trak[i].udta's child atoms
if (parsedAtoms[a_trak_atom].AtomicLevel ==
trak_udtaAtom->AtomicLevel + 1)
APar_Print_single_userdata_atomcontents(i, a_trak_atom, true);
a_trak_atom = parsedAtoms[a_trak_atom].NextAtomNumber;
}
}
}
APar_PrintUserDataAssests(true);
return;
}
///////////////////////////////////////////////////////////////////////////////////////
// Atom Tree //
///////////////////////////////////////////////////////////////////////////////////////
/*----------------------
APar_PrintAtomicTree
Following the linked list (by NextAtomNumber), list each atom as they exist
in the hieararchy, reflecting positions of moving, eliminating & additions. This
listing can occur during the course of tagging as well to assist in diagnosing
problems.
----------------------*/
void APar_PrintAtomicTree() {
bool unknown_atom = false;
char *tree_padding = (char *)malloc(
sizeof(char) *
126); // for a 25-deep atom tree (4 spaces per atom)+single space+term.
uint32_t freeSpace = 0;
short thisAtomNumber = 0;
printBOM();
// loop through each atom in the struct array (which holds the offset
// info/data)
while (true) {
AtomicInfo *thisAtom = &parsedAtoms[thisAtomNumber];
memset(tree_padding, 0, sizeof(char) * 126);
memset(twenty_byte_buffer, 0, sizeof(char) * 20);
if (thisAtom->uuid_ap_atomname != NULL) {
isolat1ToUTF8((unsigned char *)twenty_byte_buffer,
10,
(unsigned char *)thisAtom->uuid_ap_atomname,
4); // converts iso8859 <20> in '<27>ART' to a 2byte utf8 <20> glyph
} else {
isolat1ToUTF8((unsigned char *)twenty_byte_buffer,
10,
(unsigned char *)thisAtom->AtomicName,
4); // converts iso8859 <20> in '<27>ART' to a 2byte utf8 <20> glyph
}
strcpy(tree_padding, "");
if (thisAtom->AtomicLevel != 1) {
for (uint8_t pad = 1; pad < thisAtom->AtomicLevel; pad++) {
strcat(tree_padding,
" "); // if the atom depth is over 1, then add spaces before
// text starts to form the tree
}
strcat(tree_padding, " "); // add a single space
}
if (thisAtom->AtomicLength == 0) {
fprintf(stdout,
"%sAtom %s @ %" PRIu64 " of size: %" PRIu64 " (%" PRIu64
"*), ends @ %" PRIu64 "\n",
tree_padding,
twenty_byte_buffer,
thisAtom->AtomicStart,
((uint64_t)file_size - thisAtom->AtomicStart),
thisAtom->AtomicLength,
(uint64_t)file_size);
fprintf(stdout, "\t\t\t (*)denotes length of atom goes to End-of-File\n");
} else if (thisAtom->AtomicLength == 1) {
fprintf(stdout,
"%sAtom %s @ %" PRIu64 " of size: %" PRIu64
" (^), ends @ %" PRIu64 "\n",
tree_padding,
twenty_byte_buffer,
thisAtom->AtomicStart,
thisAtom->AtomicLengthExtended,
(thisAtom->AtomicStart + thisAtom->AtomicLengthExtended));
fprintf(stdout, "\t\t\t (^)denotes a 64-bit atom length\n");
// uuid atoms of any sort
} else if (thisAtom->AtomicClassification == EXTENDED_ATOM &&
thisAtom->uuid_style == UUID_DEPRECATED_FORM) {
if (UnicodeOutputStatus == WIN32_UTF16) {
fprintf(stdout, "%sAtom uuid=", tree_padding);
APar_fprintf_UTF8_data(twenty_byte_buffer);
fprintf(stdout,
" @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64 "\n",
thisAtom->AtomicStart,
thisAtom->AtomicLength,
(thisAtom->AtomicStart + thisAtom->AtomicLength));
} else {
fprintf(stdout,
"%sAtom uuid=%s @ %" PRIu64 " of size: %" PRIu64
", ends @ %" PRIu64 "\n",
tree_padding,
twenty_byte_buffer,
thisAtom->AtomicStart,
thisAtom->AtomicLength,
(thisAtom->AtomicStart + thisAtom->AtomicLength));
}
} else if (thisAtom->AtomicClassification == EXTENDED_ATOM &&
thisAtom->uuid_style != UUID_DEPRECATED_FORM) {
if (thisAtom->uuid_style == UUID_AP_SHA1_NAMESPACE) {
fprintf(stdout, "%sAtom uuid=", tree_padding);
APar_print_uuid((ap_uuid_t *)thisAtom->AtomicName, false);
fprintf(stdout,
"(APuuid=%s) @ %" PRIu64 " of size: %" PRIu64
", ends @ %" PRIu64 "\n",
twenty_byte_buffer,
thisAtom->AtomicStart,
thisAtom->AtomicLength,
(thisAtom->AtomicStart + thisAtom->AtomicLength));
} else {
fprintf(stdout, "%sAtom uuid=", tree_padding);
APar_print_uuid((ap_uuid_t *)thisAtom->AtomicName, false);
fprintf(stdout,
" @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64 "\n",
thisAtom->AtomicStart,
thisAtom->AtomicLength,
(thisAtom->AtomicStart + thisAtom->AtomicLength));
}
// 3gp assets (most of them anyway)
} else if (thisAtom->AtomicClassification == PACKED_LANG_ATOM) {
unsigned char unpacked_lang[3];
APar_UnpackLanguage(unpacked_lang, thisAtom->AtomicLanguage);
if (UnicodeOutputStatus == WIN32_UTF16) {
fprintf(stdout, "%sAtom ", tree_padding);
APar_fprintf_UTF8_data(twenty_byte_buffer);
fprintf(stdout,
" [%s] @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64
"\n",
unpacked_lang,
thisAtom->AtomicStart,
thisAtom->AtomicLength,
(thisAtom->AtomicStart + thisAtom->AtomicLength));
} else {
fprintf(stdout,
"%sAtom %s [%s] @ %" PRIu64 " of size: %" PRIu64
", ends @ %" PRIu64 "\n",
tree_padding,
twenty_byte_buffer,
unpacked_lang,
thisAtom->AtomicStart,
thisAtom->AtomicLength,
(thisAtom->AtomicStart + thisAtom->AtomicLength));
}
// all other atoms (the bulk of them will fall here)
} else {
if (UnicodeOutputStatus == WIN32_UTF16) {
fprintf(stdout, "%sAtom ", tree_padding);
APar_fprintf_UTF8_data(twenty_byte_buffer);
fprintf(stdout,
" @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64,
thisAtom->AtomicStart,
thisAtom->AtomicLength,
(thisAtom->AtomicStart + thisAtom->AtomicLength));
} else {
fprintf(stdout,
"%sAtom %s @ %" PRIu64 " of size: %" PRIu64 ", ends @ %" PRIu64,
tree_padding,
twenty_byte_buffer,
thisAtom->AtomicStart,
thisAtom->AtomicLength,
(thisAtom->AtomicStart + thisAtom->AtomicLength));
}
if (thisAtom->AtomicContainerState == UNKNOWN_ATOM_TYPE) {
for (uint8_t i = 0; i < (5 - thisAtom->AtomicLevel); i++) {
fprintf(stdout, "\t");
}
fprintf(stdout, "\t\t\t ~\n");
unknown_atom = true;
} else {
fprintf(stdout, "\n");
}
}
// simple tally & percentage of free space info
if (memcmp(thisAtom->AtomicName, "free", 4) == 0) {
freeSpace = freeSpace + thisAtom->AtomicLength;
}
// this is where the *raw* audio/video file is, the rest is
// container-related fluff.
if ((memcmp(thisAtom->AtomicName, "mdat", 4) == 0) &&
(thisAtom->AtomicLength > 100)) {
mdatData += thisAtom->AtomicLength;
} else if (memcmp(thisAtom->AtomicName, "mdat", 4) == 0 &&
thisAtom->AtomicLength == 0) { // mdat.length = 0 = ends at EOF
mdatData = file_size - thisAtom->AtomicStart;
} else if (memcmp(thisAtom->AtomicName, "mdat", 4) == 0 &&
thisAtom->AtomicLengthExtended != 0) {
mdatData +=
thisAtom->AtomicLengthExtended; // this is still adding a (limited)
// uint64_t into a uint32_t
}
if (parsedAtoms[thisAtomNumber].NextAtomNumber == 0) {
break;
} else {
thisAtomNumber = parsedAtoms[thisAtomNumber].NextAtomNumber;
}
}
if (unknown_atom) {
fprintf(stdout, "\n ~ denotes an unknown atom\n");
}
fprintf(stdout, "------------------------------------------------------\n");
fprintf(stdout, "Total size: %" PRIu64 " bytes; ", (uint64_t)file_size);
fprintf(stdout, "%i atoms total.\n", atom_number - 1);
fprintf(stdout,
"Media data: %" PRIu64 " bytes; %" PRIu64
" bytes all other atoms (%2.3lf%% atom overhead).\n",
mdatData,
file_size - mdatData,
(double)(file_size - mdatData) / (double)file_size * 100.0);
fprintf(stdout,
"Total free atom space: %" PRIu32 " bytes; %2.3lf%% waste.",
freeSpace,
(double)freeSpace / (double)file_size * 100.0);
if (freeSpace) {
dynUpd.updage_by_padding = false;
// APar_DetermineDynamicUpdate(true); //gets the size of the padding
APar_Optimize(
true); // just to know if 'free' atoms can be considered padding, or (in
// the case of say a faac file) it's *just* 'free'
if (!moov_atom_was_mooved) {
fprintf(stdout,
" Padding available: %" PRIu64 " bytes.",
dynUpd.padding_bytes);
}
}
if (gapless_void_padding > 0) {
fprintf(stdout,
"\nGapless playback null space at end of file: %" PRIu64 " bytes.",
gapless_void_padding);
}
fprintf(stdout, "\n------------------------------------------------------\n");
ShowVersionInfo();
fprintf(stdout, "------------------------------------------------------\n");
free(tree_padding);
tree_padding = NULL;
return;
}
/*----------------------
APar_SimpleAtomPrintout
print a simple flat list of atoms as they were created
----------------------*/
void APar_SimpleAtomPrintout() { // loop through each atom in the struct array
// (which holds the offset info/data)
printBOM();
for (int i = 0; i < atom_number; i++) {
AtomicInfo *thisAtom = &parsedAtoms[i];
fprintf(stdout,
"%i - Atom \"%s\" (level %u) has next atom at #%i\n",
i,
thisAtom->AtomicName,
thisAtom->AtomicLevel,
thisAtom->NextAtomNumber);
}
fprintf(stdout, "Total of %i atoms.\n", atom_number - 1);
}